做者:Faraz Kelhini翻譯:瘋狂的技術宅javascript
https://blog.logrocket.com/wh...前端
未經容許嚴禁轉載java
JavaScript 語言愈來愈被普遍地用於各類環境中。除了 Web 瀏覽器(這是 JavaScript 的最多見的宿主環境類型)以外,你還能夠在服務器,智能手機甚至機器人硬件中運行 JavaScript 程序。node
每一個環境都有其本身的對象模型,並提供了不一樣的語法來訪問全局對象。例如,在Web瀏覽器中,能夠經過 window
,self
或 frames
訪問全局對象。可是在 Node.js 中,這些屬性不存在,而你必須使用 global
。在 Web Worker 中,只有 self
可用。git
這些引用全局對象的不一樣方式使編寫可以在多個環境中工做的可移植 JavaScript 代碼變得很是困難。幸運的是,有一個正在開發中的提案打算經過引入一個名爲 globalThis
的標準屬性來解決這個問題,該屬性將在全部環境中可用。程序員
在本文中,咱們將首先研究流行的 JavaScript 環境中的全局對象,而後看看 globalThis
是如何提供一種統一的機制來訪問它。github
window
window
屬性用於在瀏覽器環境中引用當前文檔的全局對象。在代碼的頂層,使用 var
關鍵字聲明的變量將成爲 window
的屬性,而且可可以在代碼中的任何位置訪問:web
var a = [10, 20]; console.log(window.a); // → [10, 20] console.log(a === window.a); // → true
一般在使用 window
的屬性時,因爲隱含引用的緣故沒必要直接引用 window
。可是當有一個與全局變量同名的局部變量時,使用 window
是惟一的選擇:面試
var a = 10; (function() { var a = 20; console.log(a); // → 20 console.log(window.a); // → 10 })();
如你所見,不管代碼在什麼做用域內運行,window
對於引用全局對象都很是有用。注意,`window
實際上引用了 window.window
。所以,window.window === window
。segmentfault
除了標準的 JavaScript 屬性和方法以外,window
對象還包含其餘一些屬性和方法,這些屬性和方法使咱們可以控制 Web 瀏覽器窗口以及文檔自己。
self
Web Workers API沒有 window
對象,由於它沒有瀏覽上下文。相反,它提供了 WorkerGlobalScope
接口,其中包含一般由 WorkerGlobalScope
承載的數據。
爲了訪問 Web Workers 中的全局對象,咱們須要使用 self
,它是 Window
對象的 window
屬性的同義詞。與 window
相似,self
是對全局對象的引用,可用於顯式引用:
// a web worker console.log(self); // => DedicatedWorkerGlobalScope {...} var a = 10; console.log(self.a); // → 10 console.log(a === self.a); // → true
在瀏覽器環境中,此代碼將記錄 Window
而不是 DedicatedWorkerGlobalScope
。因爲 self
的值會根據使用環境的不一樣而變化,因此有時最好使用 Window
。 self
在 web worker 上下文中引用 WorkerGlobalScope.self
,而在瀏覽器上下文中引用 window.self
。
重要的是不要將 self
屬性與聲明局部變量(用於維護對上下文的引用)的常見 JavaScript 模式混淆。例如:
const obj = { myProperty: 10, myMethod: function(){ console.log(this === obj); // => true // store the value of this in a variable for use in nested functions const self = this; const helperFunction = (function() { console.log(self === obj); // => true (self refers to the outer this value) console.log(this === obj); // => false (this refers to the global object. In strict mode, it has a value of undefined) })(); } }; // invoke myMethod on the object obj. obj.myMethod();
frames
另外一種在瀏覽器環境中訪問全局對象的方法是使用 frames
屬性,該屬性的做用相似於 self
和 window
:
// browser environment console.log(frames); // => Window {...}
這個只讀屬性一般用於獲取當前窗口的子幀列表。例如你能夠用 window.frames [0]
或 frames [0]
訪問第一幀。
global
在 Node.js 中,你可使用 global
關鍵字訪問全局對象:
// node environment console.log(global); // => Object [global] {...}
window
、 self
或 frames
在 Node 環境中不起做用。請記住,Node.js 中的頂級做用域不是全局做用域。在瀏覽器中,var abc = 123
將建立一個全局變量。可是在 Node.js 中變量是模塊自己的局部變量。
this
在瀏覽器中,能夠在程序的頂層使用 this
關鍵字來引用全局對象:
this.foo = 123; console.log(this.foo === window.foo); // => true
this
在非嚴格模式下在函數或箭頭函數內也引用全局對象。可是在嚴格模式下運行的函數就不是這種狀況了,其中 this
的值爲 undefined
:
(function() { console.log(this); // => Window {...} })(); (() => { console.log(this); // => Window {...} })(); (function() { "use strict"; console.log(this); // => undefined })();
在 Node 模塊中,頂層的 this
不引用全局對象。相反,它與 module.exports
具備相同的值。在函數內部(Node 環境),this
的值取決於函數的調用方式。在 JavaScript 模塊中,頂層的 this
是 undefined
。
globalThis
globalThis
旨在經過定義標準的全局屬性來整合愈來愈分散的訪問全局對象的方式。 globalThis
提案目前處於第 4 階段,這意味着它已準備好歸入 ES2020 標準。全部流行的瀏覽器,包括 Chrome 71 +,Firefox 65+和Safari 12.1+,都已支持該功能。你也能夠在 Node.js 12+ 中使用它。
// browser environment console.log(globalThis); // => Window {...} // node.js environment console.log(globalThis); // => Object [global] {...} // web worker environment console.log(globalThis); // => DedicatedWorkerGlobalScope {...}
經過使用 globalThis
,你的代碼可以在窗口和非窗口上下文中工做,而無需編寫其餘檢查或測試代碼。在大多數環境中, globalThis
直接引用該環境的全局對象。可是在瀏覽器中,內部須要使用代理來考慮 iframe 和跨窗口安全性。實際上,它並不會改變你編寫代碼的方式。
一般,當你不肯定要在哪一種環境中使用代碼時,或者當你想使代碼在不一樣環境中可執行時,能夠用 globalThis
屬性。不過你必須用 polyfill 在不支持該功能的舊版瀏覽器上實現該功能。
另外一方面,若是須要你肯定要在什麼環境中使用代碼,請使用前面列舉引用環境全局對象的現有方法之一,避免爲 globalThis
添加 polyfill 的麻煩。
globalThis
polyfill在引入 globalThis
以前,一種經常使用的跨環境訪問全局對象的方法是使用如下模式:
function getGlobalObject() { return Function('return this')(); } if (typeof getGlobalObject().Promise.allSettled !== 'function') { // the Promise.allSettled() method is not available in this environment }
這段代碼的問題在於,在強制執行內容安全策略(CSP)的網站中不能用 Function
構造函數和 eval
。因爲CSP的緣故,Chrome 的擴展程序系統也不容許此類代碼運行。
引用全局對象的另外一種模式以下:
function getGlobalObject() { if (typeof globalThis !== 'undefined') { return globalThis; } if (typeof self !== 'undefined') { return self; } if (typeof window !== 'undefined') { return window; } if (typeof global !== 'undefined') { return global; } throw new Error('cannot find the global object'); }; if (typeof getGlobalObject().Promise.allSettled !== 'function') { // the Promise.allSettled() method is not available in this environment }
這種模式一般在 web 上使用。但也有幾個缺陷,使其在某些狀況下不可靠。幸運的是 Chrome DevTools 團隊的Mathias Bynens 提出了一種創意模式,它沒有這些缺點:
(function() { if (typeof globalThis === 'object') return; Object.defineProperty(Object.prototype, '__magic__', { get: function() { return this; }, configurable: true // This makes it possible to `delete` the getter later. }); __magic__.globalThis = __magic__; // lolwat delete Object.prototype.__magic__; }()); // Your code can use `globalThis` now. console.log(globalThis);
與其餘方法相比,polyfill 是更可靠的解決方案,但仍然不夠完美。正如 Mathias 提到的那樣,修改Object
、 Object.defineProperty
或 Object.prototype.__defineGetter__
可能會破壞 polyfill。
可以用在多種環境中的可移植 JavaScript 代碼很難編寫。每一個主機環境都有一個略有不一樣的對象模型。所以,要訪問全局對象,你須要在不一樣的 JavaScript 環境中使用不一樣的語法。
經過引入 globalThis
屬性,訪問全局對象將變得更加簡單,而且再也不須要去檢測代碼所運行的環境。
乍一看 globalThis
彷佛很容易實現。可是實際上,正確地進行操做是很是複雜的。現有的解決方法都不完美,若是不當心就可能會引入錯誤。
ECMAScript 正在迅速發展,你能夠指望它可以更多地引入新功能。要獲取有關規範最新添加的更新,請查看完成的提案列表。