Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

global对象获取 #44

Open
bosens-China opened this issue Jan 9, 2020 · 0 comments
Open

global对象获取 #44

bosens-China opened this issue Jan 9, 2020 · 0 comments
Labels
JavaScript JavaScript系列的文章

Comments

@bosens-China
Copy link
Owner

bosens-China commented Jan 9, 2020

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,对作者也是一种鼓励。

参考链接

@bosens-China bosens-China added the JavaScript JavaScript系列的文章 label Jan 9, 2020
@bosens-China bosens-China changed the title 详解global对象的获取 global对象获取 Dec 21, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
JavaScript JavaScript系列的文章
Projects
None yet
Development

No branches or pull requests

1 participant