13161216443

您所在位置: 首頁> 學習課程> web前端培訓 | 用 globalThis 訪問全局對象

web前端培訓 | 用 globalThis 訪問全局對象

發布百知教育 來源:學習課程 2019-12-05

JavaScript 語言越來越被廣泛地用于各種環境中。除了 Web 瀏覽器(這是 JavaScript 的最常見的宿主環境類型)之外,你還可以在服務器,智能手機甚至機器人硬件中運行 JavaScript 程序。

每個環境都有其自己的對象模型,并提供了不同的語法來訪問全局對象。例如,在Web瀏覽器中,可以通過 window,self  frames 訪問全局對象。但是在 Node.js 中,這些屬性不存在,而你必須使用 global。在 Web Worker 中,只有 self 可用。

這些引用全局對象的不同方式使編寫能夠在多個環境中工作的可移植 JavaScript 代碼變得非常困難。幸運的是,有一個正在開發中的提案【https://github.com/tc39/proposal-global】打算通過引入一個名為 globalThis 的標準屬性來解決這個問題,該屬性將在所有環境中可用。

在本文中,我們將首先研究流行的 JavaScript 環境中的全局對象,然后看看 globalThis 是如何提供一種統一的機制來訪問它。

`window`

window 屬性用于在瀏覽器環境中引用當前文檔的全局對象。在代碼的頂層,使用 var 關鍵字聲明的變量將成為 window 的屬性,并且可能夠在代碼中的任何位置訪問:

1var a = [1020];
2
3console.log(window.a);          // → [10, 20]
4console.log(a === window.a);    // → true

通常在使用 window 的屬性時,由于隱含引用的緣故不必直接引用 window。但是當有一個與全局變量同名的局部變量時,使用 window 是唯一的選擇:

1var a = 10;
2
3(function({
4  var a = 20;   
5  console.log(a);           // → 20
6  console.log(window.a);    // → 10
7})();

如你所見,無論代碼在什么作用域內運行,window 對于引用全局對象都非常有用。注意,`window實際上引用了 window.window。因此,window.window === window。

除了標準的 JavaScript 屬性和方法之外,window 對象還包含其他一些屬性和方法,這些屬性和方法使我們能夠控制 Web 瀏覽器窗口以及文檔本身。

`self`

Web Workers API沒有 window 對象,因為它沒有瀏覽上下文。相反,它提供了WorkerGlobalScope 接口,其中包含通常由 WorkerGlobalScope 承載的數據。

為了訪問 Web Workers 中的全局對象,我們需要使用 self,它是 Window 對象的 window 屬性的同義詞。與 window 類似,self 是對全局對象的引用,可用于顯式引用:

1// a web worker
2console.log(self);    // => DedicatedWorkerGlobalScope {...}
3
4var a = 10;
5
6console.log(self.a);          // → 10
7console.log(a === self.a);    // → true

在瀏覽器環境中,此代碼將記錄 Window 而不是 DedicatedWorkerGlobalScope。由于 self的值會根據使用環境的不同而變化,所以有時最好使用 Window。self 在 web worker 上下文中引用 WorkerGlobalScope.self,而在瀏覽器上下文中引用 window.self。

重要的是不要將 self 屬性與聲明局部變量(用于維護對上下文的引用)的常見 JavaScript 模式混淆。例如:

 1const obj = {
2  myProperty10,
3  myMethodfunction(){
4    console.log(this === obj);    // => true
5
6    // store the value of this in a variable for use in nested functions
7    const self = this;
8
9    const helperFunction = (function({
10      console.log(self === obj);  // => true (self refers to the outer this value)
11      console.log(this === obj);  // => false (this refers to the global object. In strict mode, it has a value of undefined)
12    })();
13  }
14};
15
16// invoke myMethod on the object obj.
17obj.myMethod();

`frames`

另一種在瀏覽器環境中訪問全局對象的方法是使用 frames 屬性,該屬性的作用類似于 self window

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

這個只讀屬性通常用于獲取當前窗口的子幀列表。例如你可以用 window.frames [0] frames [0] 訪問第一幀。


`global`

在 Node.js 中,你可以使用 global 關鍵字訪問全局對象:

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

window、 self  frames 在 Node 環境中不起作用。請記住,Node.js 中的頂級作用域不是全局作用域。在瀏覽器中,var abc = 123 將創建一個全局變量。但是在 Node.js 中變量是模塊本身的局部變量。

`this`

在瀏覽器中,可以在程序的頂層使用 this 關鍵字來引用全局對象:

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

this 在非嚴格模式下在函數或箭頭函數內也引用全局對象。但是在嚴格模式下運行的函數就不是這種情況了,其中 this 的值為 undefined

 1(function({
2  console.log(this);    // => Window {...}
3})();
4
5(() => {
6  console.log(this);    // => Window {...}
7})();
8
9(function({
10  "use strict";
11  console.log(this);    // => undefined
12})();

在 Node 模塊中,頂層的 this 不引用全局對象。相反,它與 module.exports 具有相同的值。在函數內部(Node 環境),this 的值取決于函數的調用方式。在 JavaScript 模塊中,頂層的 this  undefined。

介紹 `globalThis`

globalThis 旨在通過定義標準的全局屬性來整合越來越分散的訪問全局對象的方式。globalThis 提案目前處于第 4 階段,這意味著它已準備好納入 ES2020 標準。所有流行的瀏覽器,包括 Chrome 71 +,Firefox 65+和Safari 12.1+,都已支持該功能。你也可以在 Node.js 12+ 中使用它。

1// browser environment
2console.log(globalThis);    // => Window {...}
3
4// node.js environment
5console.log(globalThis);    // => Object [global] {...}
6
7// web worker environment
8console.log(globalThis);    // => DedicatedWorkerGlobalScope {...}

通過使用 globalThis,你的代碼能夠在窗口和非窗口上下文中工作,而無需編寫其他檢查或測試代碼。在大多數環境中, globalThis 直接引用該環境的全局對象。但是在瀏覽器中,內部需要使用代理來考慮 iframe 和跨窗口安全性。實際上,它并不會改變你編寫代碼的方式。

通常,當你不確定要在哪種環境中使用代碼時,或者當你想使代碼在不同環境中可執行時,可以用 globalThis 屬性。不過你必須用 polyfill 在不支持該功能的舊版瀏覽器上實現該功能。

另一方面,如果需要你確定要在什么環境中使用代碼,請使用前面列舉引用環境全局對象的現有方法之一,避免為 globalThis 添加 polyfill 的麻煩。


創建一個 `globalThis` polyfill

在引入 globalThis 之前,一種常用的跨環境訪問全局對象的方法是使用以下模式:

1function getGlobalObject({
2  return Function('return this')();
3}
4
5if (typeof getGlobalObject().Promise.allSettled !== 'function') {
6  // the Promise.allSettled() method is not available in this environment
7}

這段代碼的問題在于,在強制執行內容安全策略(CSP)【https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP】的網站中不能用 Function 構造函數和 eval。由于CSP的緣故,Chrome 的擴展程序系統也不允許此類代碼運行。

引用全局對象的另一種模式如下:

 1function getGlobalObject({
2  if (typeof globalThis !== 'undefined') { return globalThis; }
3  if (typeof self !== 'undefined') { return self; }
4  if (typeof window !== 'undefined') { return window; }
5  if (typeof global !== 'undefined') { return global; }
6  throw new Error('cannot find the global object');
7};
8
9if (typeof getGlobalObject().Promise.allSettled !== 'function') {
10  // the Promise.allSettled() method is not available in this environment
11}

這種模式通常在 web 上使用。但也有幾個缺陷【https://mathiasbynens.be/notes/globalthis#naive-polyfill】,使其在某些情況下不可靠。幸運的是 Chrome DevTools 團隊的Mathias Bynens 提出了一種創意模式【https://mathiasbynens.be/notes/globalthis#robust-polyfill】,它沒有這些缺點:

 1(function({
2  if (typeof globalThis === 'object'return;
3  Object.defineProperty(Object.prototype, '__magic__', {
4    getfunction({
5      return this;
6    },
7    configurabletrue // This makes it possible to `delete` the getter later.
8  });
9  __magic__.globalThis = __magic__; // lolwat
10  delete Object.prototype.__magic__;
11}());
12
13// Your code can use `globalThis` now.
14console.log(globalThis);

與其他方法相比,polyfill 是更可靠的解決方案,但仍然不夠完美。正如  Mathias 提到的那樣,修改Object、 Object.defineProperty 或  Object.prototype.__defineGetter__  可能會破壞 polyfill。

總結

能夠用在多種環境中的可移植 JavaScript 代碼很難編寫。每個主機環境都有一個略有不同的對象模型。因此,要訪問全局對象,你需要在不同的 JavaScript 環境中使用不同的語法。

通過引入 globalThis 屬性,訪問全局對象將變得更加簡單,并且不再需要去檢測代碼所運行的環境。

乍一看 globalThis 似乎很容易實現。但是實際上,正確地進行操作是非常復雜的?,F有的解決方法都不完美,如果不小心就可能會引入錯誤。


web前端培訓班:http://www.akpsimsu.com/web2019


注釋:本文來自公眾號前端先鋒

上一篇:大數據發展迅速,初學者還能成大數據“稀缺人才”嗎?

下一篇:應屆生去公司找個Java程序員的職位需要什么技能?

相關推薦

www.akpsimsu.com

有位老師想和您聊一聊

關閉

立即申請