用 global This访问全局对象

更新日期: 2019-11-04阅读: 2.4k标签: 对象

JavaScript 语言越来越被广泛地用于各种环境中。除了 Web 浏览器(这是 JavaScript 的最常见的宿主环境类型)之外,你还可以在服务器,智能手机甚至机器人硬件中运行 JavaScript 程序。

每个环境都有其自己的对象模型,并提供了不同的语法来访问全局对象。例如,在Web浏览器中,可以通过 window,self 或 frames 访问全局对象。但是在 Node.js 中,这些属性不存在,而你必须使用 global。在 Web Worker 中,只有 self 可用。

这些引用全局对象的不同方式使编写能够在多个环境中工作的可移植 JavaScript 代码变得非常困难。幸运的是,有一个正在开发中的提案打算通过引入一个名为 globalThis 的标准属性来解决这个问题,该属性将在所有环境中可用。

在本文中,我们将首先研究流行的 JavaScript 环境中的全局对象,然后看看 globalThis 是如何提供一种统一的机制来访问它。


window

window 属性用于在浏览器环境中引用当前文档的全局对象。在代码的顶层,使用 var 关键字声明的变量将成为 window 的属性,并且可能够在代码中的任何位置访问:

var a = [10, 20];

console.log(window.a);          // → [10, 20]
console.log(a === window.a);    // → true

通常在使用 window 的属性时,由于隐含引用的缘故不必直接引用 window。但是当有一个与全局变量同名的局部变量时,使用 window 是唯一的选择:

var a = 10;

(function() {
  var a = 20;   
  console.log(a);           // → 20
  console.log(window.a);    // → 10
})();

如你所见,无论代码在什么作用域内运行,window 对于引用全局对象都非常有用。注意,`window实际上引用了 window.window。因此,window.window === window。

除了标准的 JavaScript 属性和方法之外,window 对象还包含其他一些属性和方法,这些属性和方法使我们能够控制 Web 浏览器窗口以及文档本身。


self

Web Workers api没有 window 对象,因为它没有浏览上下文。相反,它提供了 WorkerGlobalScope 接口,其中包含通常由 WorkerGlobalScope 承载的数据

为了访问 Web Workers 中的全局对象,我们需要使用 self,它是 Window 对象的 window 属性的同义词。与 window 类似,self 是对全局对象的引用,可用于显式引用:

// a web worker
console.log(self);    // => DedicatedWorkerGlobalScope {...}

var a = 10;

console.log(self.a);          // → 10
console.log(a === self.a);    // → true

在浏览器环境中,此代码将记录 Window 而不是 DedicatedWorkerGlobalScope。由于 self 的值会根据使用环境的不同而变化,所以有时最好使用 Window。 self 在 web worker 上下文中引用 WorkerGlobalScope.self,而在浏览器上下文中引用 window.self。

重要的是不要将 self 属性与声明局部变量(用于维护对上下文的引用)的常见 JavaScript 模式混淆。例如:

const obj = {
  myProperty: 10,
  myMethod: function(){
    console.log(this === obj);    // => true

    // store the value of this in a variable for use in nested functions
    const self = this;

    const helperFunction = (function() {
      console.log(self === obj);  // => true (self refers to the outer this value)
      console.log(this === obj);  // => false (this refers to the global object. In strict mode, it has a value of undefined)
    })();
  }
};

// invoke myMethod on the object obj.
obj.myMethod();


frames

另一种在浏览器环境中访问全局对象的方法是使用 frames 属性,该属性的作用类似于 self 和 window:

// browser environment
console.log(frames);    // => Window {...}

这个只读属性通常用于获取当前窗口的子帧列表。例如你可以用 window.frames [0] 或 frames [0] 访问第一帧。


global

在 Node.js 中,你可以使用 global 关键字访问全局对象:

// node environment
console.log(global);    // => Object [global] {...}

window、 self 或 frames 在 Node 环境中不起作用。请记住,Node.js 中的顶级作用域不是全局作用域。在浏览器中,var abc = 123 将创建一个全局变量。但是在 Node.js 中变量是模块本身的局部变量。


this

在浏览器中,可以在程序的顶层使用 this 关键字来引用全局对象:

this.foo = 123;
console.log(this.foo === window.foo);    // => true

this 在非严格模式下在函数或箭头函数内也引用全局对象。但是在严格模式下运行的函数就不是这种情况了,其中 this 的值为 undefined:

(function() {
  console.log(this);    // => Window {...}
})();

(() => {
  console.log(this);    // => Window {...}
})();

(function() {
  "use strict";
  console.log(this);    // => undefined
})();

在 Node 模块中,顶层的 this 不引用全局对象。相反,它与 module.exports 具有相同的值。在函数内部(Node 环境),this 的值取决于函数的调用方式。在 JavaScript 模块中,顶层的 this 是 undefined。


介绍 globalThis

globalThis 旨在通过定义标准的全局属性来整合越来越分散的访问全局对象的方式。 globalThis 提案目前处于第 4 阶段,这意味着它已准备好纳入 ES2020 标准。所有流行的浏览器,包括 Chrome 71 +,Firefox 65+和Safari 12.1+,都已支持该功能。你也可以在 Node.js 12+ 中使用它。

// browser environment
console.log(globalThis);    // => Window {...}

// node.js environment
console.log(globalThis);    // => Object [global] {...}

// web worker environment
console.log(globalThis);    // => DedicatedWorkerGlobalScope {...}

通过使用 globalThis,你的代码能够在窗口和非窗口上下文中工作,而无需编写其他检查或测试代码。在大多数环境中, globalThis 直接引用该环境的全局对象。但是在浏览器中,内部需要使用代理来考虑 iframe 和跨窗口安全性。实际上,它并不会改变你编写代码的方式。

通常,当你不确定要在哪种环境中使用代码时,或者当你想使代码在不同环境中可执行时,可以用 globalThis 属性。不过你必须用 polyfill 在不支持该功能的旧版浏览器上实现该功能。

另一方面,如果需要你确定要在什么环境中使用代码,请使用前面列举引用环境全局对象的现有方法之一,避免为 globalThis 添加 polyfill 的麻烦。


创建一个 globalThis polyfill

在引入 globalThis 之前,一种常用的跨环境访问全局对象的方法是使用以下模式:

function getGlobalObject() {
  return Function('return this')();
}

if (typeof getGlobalObject().Promise.allSettled !== 'function') {
  // the Promise.allSettled() method is not available in this environment
}

这段代码的问题在于,在强制执行内容安全策略(CSP)网站中不能用 Function 构造函数和 eval。由于CSP的缘故,Chrome 的扩展程序系统也不允许此类代码运行。

引用全局对象的另一种模式如下:

function getGlobalObject() {
  if (typeof globalThis !== 'undefined') { return globalThis; }
  if (typeof self !== 'undefined') { return self; }
  if (typeof window !== 'undefined') { return window; }
  if (typeof global !== 'undefined') { return global; }
  throw new Error('cannot find the global object');
};

if (typeof getGlobalObject().Promise.allSettled !== 'function') {
  // the Promise.allSettled() method is not available in this environment
}

这种模式通常在 web 上使用。但也有几个缺陷,使其在某些情况下不可靠。幸运的是 Chrome DevTools 团队的Mathias Bynens 提出了一种创意模式,它没有这些缺点:

(function() {
  if (typeof globalThis === 'object') return;
  Object.defineProperty(Object.prototype, '__magic__', {
    get: function() {
      return this;
    },
    configurable: true // This makes it possible to `delete` the getter later.
  });
  __magic__.globalThis = __magic__; // lolwat
  delete Object.prototype.__magic__;
}());

// Your code can use `globalThis` now.
console.log(globalThis);

与其他方法相比,polyfill 是更可靠的解决方案,但仍然不够完美。正如 Mathias 提到的那样,修改Object、 Object.defineProperty 或 Object.prototype.__defineGetter__ 可能会破坏 polyfill。


总结

能够用在多种环境中的可移植 JavaScript 代码很难编写。每个主机环境都有一个略有不同的对象模型。因此,要访问全局对象,你需要在不同的 JavaScript 环境中使用不同的语法。

通过引入 globalThis 属性,访问全局对象将变得更加简单,并且不再需要去检测代码所运行的环境。

乍一看 globalThis 似乎很容易实现。但是实际上,正确地进行操作是非常复杂的。现有的解决方法都不完美,如果不小心就可能会引入错误。

ECMAScript 正在迅速发展,你可以期望它能够更多地引入新功能。要获取有关规范最新添加的更新,请查看完成的提案列表。


链接: https://www.fly63.com/article/detial/6724

JavaScript中创建对象的7种模式

ECMA-262把对象定义为:”无需属性的集合,其属性可以包含基本值、对象或者函数。对象的每个属性或方法都有一个名字,而每个名字都映射到一个值。正因为这样,我们可以把ECMAScript的对象想象成散列表:无非就是一组名对值,其中值可以是数据或函数。

JavaScript数组、对象合并的多种方法实现

这篇文章讲解Js数组和对象的一些使用技巧,如何将不同的数组,对象合并/结合为1个的方法

Js通过.或者[]访问对象属性的语法、性能等区别

在JavaScript中可以使用 . 或者 [ ] 来访问对象的属性,但是对象中方法只能通过 . 来获取;使用.运算符来存取对象的属性的值。或者使用[]作为一个关联数组来存取对象的属性。但是这两种方式有什么区别了?

js中关于for...in遍历对象属性的顺序问题

对象使用obj.length时,它得到的值是undefined的,所以只能通过for...in循环获取对象的属性,我们发现并没有按属性的顺序显示,而且顺序在各个浏览器下显示也不同。 这是为什么呢?

JS声明对象时属性名加引号与不加引号的问题

JS声明对象时属性名加引号与不加引号的问题,一般情况下属性名加引号和不加引号是都可以的,效果是一样的。如果属性名是数字,则必须用“”包围,并且用 [] 方括号访问。

javascript的本地对象,内置对象和宿主对象

javascript的原生对象:也叫内部对象、本地对象、native object;内置对象:Global(全局对象)、Math ;宿主对象:有宿主提供的对象,在浏览器中window对象以及其下边所有的子对象(如bom、dom等等),在node中是globla及其子对象,也包含自定义的类对象。

JavaScript 判断对象中是否有某属性

判断对象中是否有某属性的常见方式总结,不同的场景要使用不同的方式。一点( . )或者方括号( [ ] )、二in 运算符、三hasOwnProperty()。三种方式各有优缺点,不同的场景使用不同的方式,有时还需要结合使用

JavaScript Error对象详解

error,指程序中的非正常运行状态,在其他编程语言中称为“异常”或“错误”。解释器会为每个错误情形创建并抛出一个Error对象,其中包含错误的描述信息。

如何禁止JavaScript对象重写?

由于JavaScript的灵活性,我们可以轻易地重写(override)一些于其他人定义的对象(object)。换句话说,任何人都可以重写我们所定义的对象。这是一个非常强大的特性,许多开发者都有兴趣试试,来拓展或者修改某些对象的行为。

JavaScript面向对象编程中_优雅的类写法

虽然现在已经是ES6的时代,但是,还是有必要了解下ES5是怎么写一个类的。本文详述JavaScript面向对象编程中的类写法,并分步骤讲述如何写出优雅的类。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!