Jordan Harband 的 ECMAScript 提案“global
”现在处于第三阶段。该提案提供了一种新的用于访问全局对象的标准方式。
全局对象的引用
下面是常用的几种引用全局对象的方式:
- 全局变量:
- 全局变量
window
: 这是经典的全局对象。但是在 Node.js 和 Web Worker 里不可用。 - 全局变量
self
: 在 Web Worker 和一般浏览器中可用。但是 Node.js 不支持。有一些人使用self
来使得代码能在 Web Workers 和一般浏览器的环境中都能生效。 - 全局变量
global
: 至今仅仅只有 Node.js 使用!
- 全局变量
-
this
:-
全局作用域里的 this
: 指向全局对象。唯一的问题是 Node.js 模块和 ES6 模块有它们自己的作用域,而这意味着这里的 this 不会指向全局对象。 -
非严格模式下函数调用里的 this
: 如果你在非严格模式下调用一个函数(不是方法调用),函数的this
会指向全局对象。在严格模式下,this 是undefined。
-
new Function('return this')()
: 在严格模式和非严格模式下同样有效,因为new Function()
的参数总是在非严格模式下执行。下面是一个严重的警告:当你使用CSP (Content Security Policy)的时候,eval
,new Function() 等等是不可用的。这使得这种返回 this 的方法在许多情况下不适宜使用。
-
提案
该 ECMAScript 提案提议:将全局变量 global
作为访问全局对象的标准方式。该提案也提议: Object.prototype
必须在全局对象的原型链里。下面的代码在现在的浏览器上是 true:
> Object.prototype.isPrototypeOf(window)
true
最佳实践
现在,由于向后兼容的缘故,全局对象被认为是一个 JavaScript 不能摆脱错误。它影响性能,且通常是令人困惑的。
ECMAScript 6 提供了下面不会在全局作用域里创建全局属性(var 声明和函数声明会)
的三种新的声明变量的方式来避免:
-
let
声明 -
const
声明 -
Class
声明
换句话说:全局对象的所有属性是全局变量,但不是所有的全局变量都是全局对象的属性。比如(在全局作用域执行):
> var foo;
> 'foo' in window
true > let bar;
> 'bar' in window
false
通常情况下将全局变量只作为变量好于将其作为全局对象的属性,比如 window。这样就能在所有
JavaScript 平台上都有效。
另外,从 ES6 开始(甚至在这之前),多数 JavaScript 代码都存在模块里且永远都不会出现在全局作用域里。
因此,global
将大多时候与 polyfills 相关(global
will mostly be relevant for polyfills)。
A polyfill
本提案的作者 Jordan Harband 为此写了一个polyfill。
CommonJS 语法:
var global = require('system.global')();
ES6 模块语法:
import getGlobal from 'system.global';
const global = getGlobal();
这个包使用的是“最原生的”可用的访问方法(比如 Node.js 的 global,一般浏览器环境的
window
等等)。
获得全局对象的引用
在代码内部,这个 polyfill 使用 getPolyfill() 函数来计算得全局对象的引用。以下是实现方式:
// polyfill.js
var implementation = require('./implementation');
module.exports = function getPolyfill() {
if (typeof global !== 'object' || !global || global.Math !== Math
|| global.Array !== Array) {
return implementation;
}
return global;
}; // implementation.js
if (typeof self !== 'undefined') {
module.exports = self;
} else if (typeof window !== 'undefined') {
module.exports = window;
} else if (typeof global !== 'undefined') {
module.exports = global;
} else {
module.exports = Function('return this')();
}