做者: Dmitri Pavlutin
譯者:前端小智
來源:news
點贊再看,養成習慣本文
GitHub
https://github.com/qq44924588... 上已經收錄,更多往期高贊文章的分類,也整理了不少個人文檔,和教程資料。歡迎Star和完善,你們面試能夠參照考點複習,但願咱們一塊兒有點東西。前端
JS 語言愈來愈多被用於各類環境中。除了最多見的瀏覽器以外,它還能夠在服務器、智能手機甚至機器人硬件上運行。node
每一個環境都有其本身的對象模型,並提供了不一樣的語法來訪問全局對象。 例如,在 Web 瀏覽器中,能夠經過window
,self
或frames
訪問全局對象。 可是,在 Node.js 中,這些屬性不存在,而必須使用global
。 在Web Worker中,只有self
可用。git
這些不一樣的全局對象引用方法讓 JS 實現跨平臺變得很是困難。幸運的是,有一個提案正在制定中,旨在經過引入一個名爲globalThis
的標準屬性來解決這個問題,該屬性將在全部環境中可用。github
在本文中,咱們首先研究一下 JS 環境中的全局對象,而後瞭解globalThis
如何提供統一的機制來訪問它。web
window
屬性用於在瀏覽器環境中引用當前文檔的全局對象。在代碼的頂層,使用var
關鍵字聲明的變量將成爲window
的屬性,而且能夠從代碼中的任何位置進行訪問:面試
var a = [10, 20]; console.log(window.a); // → [10, 20] console.log(a === window.a); // → true
一般,在使用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
。安全
除了標準的 JS 屬性和方法外,window
對象還包含幾個額外的屬性和方法,這些屬性和方法容許咱們控制web瀏覽器窗口和文檔自己。服務器
Web Workers API沒有Window
對象,由於它沒有瀏覽上下文。相反,它提供WorkerGlobalScope
接口,其中包含相似window
攜帶的數據。微信
要訪問Web Workers中的全局對象,咱們使用self
,它是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
屬性與聲明局部變量(用於維護對上下文的引用)的通用 JS 模式相混淆,這一點很重要。例如
const obj = { myProperty: 10, myMethod: function(){ console.log(this === obj); // => true // 將 this 值存儲在變量中,以便在嵌套函數中使用 const self = this; const helperFunction = (function() { console.log(self === obj); // => true (self 指的是外部的 this 值) console.log(this === obj); // => false (this 指的是全局對象。在嚴格模式下,它的值爲undefined) })(); } }; obj.myMethod();
在瀏覽器環境中訪問全局對象的另外一種方法是使用frames
屬性,其工做方式相似於self
和window
:
// browser environment console.log(frames); // => Window {...}
此只讀屬性一般用於獲取當前窗口的子幀列表。例如,咱們可使用window.frames[0]
或frames[0]
來訪問第一幀。
在Node.js中,可使用global
關鍵字訪問全局對象:
// node 環境 console.log(global); // => Object [global] {...}
window
、self
或frames
在 Node 環境中不起做用。請記住Node.js中的頂級做用域不是全局做用域。在瀏覽器中,var abc = 123
將建立一個全局變量。 可是,在 Node.js 中,變量將是模塊自己的局部變量。
在瀏覽器中,能夠在程序的頂層使用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
值是根據調用函數的方式肯定的。在 JS 模塊中,this 在頂層是undefined
的。
globalThis
旨在經過定義一個標準的全局屬性來整合日益分散的訪問全局對象的方法。該提案目前處於第四階段,這意味着它已經準備好被歸入ES2020標準。全部流行的瀏覽器,包括Chrome 71+、Firefox 65+和Safari 12.1+,都已經支持這項功能。你也能夠在Node.js 12+中使用它。
// 瀏覽器環境 console.log(globalThis); // => Window {...} // node.js 環境 console.log(globalThis); // => Object [global] {...} // web worker 環境 console.log(globalThis); // => DedicatedWorkerGlobalScope {...}
經過使用globalThis
,你的代碼將在 window 和非 window 上下文中工做,而無需編寫額外的檢查或測試。在大多數環境中,globalThis
直接引用該環境的全局對象。可是,在瀏覽器中,內部使用代理來考慮iframe
和跨 window 安全性。 實際上,它並不會改變咱們編寫代碼的方式。
另外一方面,若是你肯定你的代碼將在什麼環境中使用,那麼可使用現有的方法來引用環境的全局對象,這樣就沒必要爲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 }
此模式一般在網絡上使用。 但這也有一些缺陷,使其在某些狀況下不可靠。 幸運的是,Chrome開發者工具(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。
編寫可在多種環境下工做的可移植 JS 代碼是很困難的。每一個宿主環境都有稍微不一樣的對象模型。所以,要訪問全局對象,須要在不一樣的 JS 環境中使用不一樣的語法。
隨着globalThis
屬性的引入,訪問全局對象將變得更加簡單,而且再也不須要檢測代碼運行的環境。
乍一看,globalThis
的實現很容易,可是在實踐中,倒是很複雜的。全部現有的實現方案都是不完美的,若是不當心的話可能會引入 bug。
代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具 Fundebug。
原文:https://news.ycombinator.com/...
文章每週持續更新,能夠微信搜索「 大遷世界 」第一時間閱讀和催更(比博客早一到兩篇喲),本文 GitHub https://github.com/qq449245884/xiaozhi 已經收錄,整理了不少個人文檔,歡迎Star和完善,你們面試能夠參照考點複習,另外關注公衆號,後臺回覆福利,便可看到福利,你懂的。