详解global对象的获取

2,236 阅读4分钟

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.globalwindow.window都是指向自身,可以理解为无限嵌套的属性

Web Worker

Web Worker 是为 JavaScript 创造多线程环境出现的,不过使用它是有一些限制无法使用 document、window、parent等这些对象,所以在上面的例子中如果我们在 Web Worker 环境中一定会报错,因为不存在 globalwindow 对象

不过 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._ = _);
})();

微信小程序

在微信小程序中globalwindow都不存在再加上使用的是严格模式,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,对作者也是一种鼓励。

参考链接