global对象获取
global 在开发中经常经常使用,例如下面一段代码调用的就是 global 内置的对象
const min = Math.min(...[1, 11, 22, 55, -2, -1]);
这里的 Math 对象就是 global 内置的,在浏览器环境下我们可以通过window.Math.min
显示调用,而在 node 环境下我们则要通过global.Math.min
来调用,在实际中我们不会通过window.Math.min
这种方式来调用,不过却也能看到不同环境下获取 global 对象各不相同。
下面就以编写一个现代的工具库为假设,这个库要支持全局引用也是很科学的,但是如何让其挂载在全局属性上呢?
第一版
(function() {
var _global = this;
var _ = {};
_.debounce = function() {};
// ...一些其他的方法
return (_global._ = _);
})();
这里的想法是通过全局环境下运行 this 来返回一个全局对象,在全局对象上挂载我们的工具函数,不通过 window
显示挂载是因为我们不仅要让这个工具库运行在浏览器环境下,同时也让他运行在 node
环境中。
严格模式
上面的写法是假设在非严格模式下运行,而在严格模式下运行,this 会返回一个undefined
(function() {
"use strict";
console.log(this === undefined); // true
})();
下面我们来尝试修订一下严格模式下的错误
(function() {
// 防止null出现
function isObjectLike(par) {
return par && typeof par == "object";
}
var _global;
if (isObjectLike(global) && global.global === global) {
_global = global;
} else {
_global = window;
}
var _ = {};
_.debounce = function() {};
// ...一些其他的方法
return (_global._ = _);
})();
这里我们通过判断 global 是否存在,如果存在就是 node
环境如果不存在就是浏览器环境。
global.global
和window.window
都是指向自身,可以理解为无限嵌套的属性
Web Worker
Web Worker 是为 JavaScript 创造多线程环境出现的,不过使用它是有一些限制无法使用 document、window、parent
等这些对象,所以在上面的例子中如果我们在 Web Worker 环境中一定会报错,因为不存在 global
和 window
对象
不过 Worker 可以通过self
来拿到子线程的全局对象,而且 self 在浏览器环境下也指向 window
(function() {
// 防止null出现
function isObjectLike(par) {
return par && typeof par == "object";
}
var _global;
if (isObjectLike(global) && global.global === global) {
_global = global;
} else if (isObjectLike(self) && self === self) {
_global = self;
}
var _ = {};
_.debounce = function() {};
// ...一些其他的方法
return (_global._ = _);
})();
node 虚拟机
node vm(沙盒) 环境下不存在 global 和 window 对象,所以上面代码还是会出现问题,不过我们可以通过
new Function('return this')()
或者this
的形式来解决
(function() {
// 防止null出现
function isObjectLike(par) {
return par && typeof par == "object";
}
var _global;
if (isObjectLike(global) && global.global === global) {
_global = global;
} else if (isObjectLike(self) && self === self) {
_global = self;
} else {
_global = new Function("return this")();
// 或者
// _global = this;
}
var _ = {};
_.debounce = function() {};
// ...一些其他的方法
return (_global._ = _);
})();
Content Security Policy
上面你可能注意到了,我在上一版提到了两个方法来最后获取 global
的值
- _global = new Function("return this")();
- _global = this;
不过在网页安全政策(Content Security Policy)下只会加载信任的白名单,eval、new Function
这些方法都可能无法使用,只能使用\_global = this
来获取 global
(function() {
// 防止null出现
function isObjectLike(par) {
return par && typeof par == "object";
}
var _global;
if (isObjectLike(global) && global.global === global) {
_global = global;
} else if (isObjectLike(self) && self === self) {
_global = self;
} else {
// 或者
_global = this;
}
var _ = {};
_.debounce = function() {};
// ...一些其他的方法
return (_global._ = _);
})();
微信小程序
在微信小程序中global
和window
都不存在再加上使用的是严格模式,this 会返回undefined
所以我们还需要在加一个判断
(function() {
// 防止null出现
function isObjectLike(par) {
return par && typeof par == "object";
}
var _global;
if (isObjectLike(global) && global.global === global) {
_global = global;
} else if (isObjectLike(self) && self === self) {
_global = self;
} else {
// 或者
_global = this || {};
}
var _ = {};
_.debounce = function() {};
// ...一些其他的方法
return (_global._ = _);
})();
globalThis
上面的判断方式是现在社区主流做法,不过在 tc39 的提案中globalThis
可以获取全局对象,使用方法也很简单
// 浏览器环境
globalThis === window; // true
// node
globalThis === global; // true
目前是Stage 3
使用的话还是需要做一些兼容性的处理,下面就来写最后一版
(function() {
// 防止null出现
function isObjectLike(par) {
return par && typeof par == "object";
}
function getGlobal() {
if (isObjectLike(globalThis) && globalThis.Object === Object) {
return globalThis;
}
if (isObjectLike(global) && global.global === global) {
return global;
}
if (isObjectLike(self) && self === self) {
return self;
}
return this || {};
}
var _global = getGlobal();
var _ = {};
_.debounce = function() {};
// ...一些其他的方法
return (_global._ = _);
})();
最后
本来可以一次写完,不过还是希望循循而进了解写了这么多判断究竟是为什么,最后如果有不正确的地方希望有小伙伴指出来,欢迎 star,对作者也是一种鼓励。