原文: https://2ality.com/2019/07/global-scope.html翻譯: 劉小夕
在這篇博文中,我們將研究 JAVAScript 的全局變量是如何工作的。如: scripts的范圍,所謂的全局對象等等。
1.作用域
變量的詞法作用域(簡稱:作用域)是可以訪問它的程序的區(qū)域。 JavaScript 的作用域是靜態(tài)的(它們在運行時不會改變)并且它們可以嵌套 - 例如:
function func () { // (A) const foo = 1 ; if ( true ) { // (B) const bar = 2 ; } }
if 語句引入的作用域(行B)嵌套在函數(shù) func()(行A)的作用域內(nèi)。
在示例中, func 是 if 的外層作用域。
2.詞法作用域
在 JavaScript 語言規(guī)范中,作用域是通過詞法作用域“實現(xiàn)”的。它們由兩部分組成:
- 將變量名映射到變量值的環(huán)境記錄(可以想象成是字典)。這是 JavaScript 存儲變量的地方。環(huán)境記錄中的一個 key-value 條目稱為綁定。
- 對外部環(huán)境的引用 - 表示當前環(huán)境所代表的作用域的外部作用域的環(huán)境。
因此,嵌套作用域樹可以由嵌套環(huán)境樹表示。
3.全局對象
全局對象是一個對象,其屬性是全局變量。
- 無處不在: globalThis
- 全局對象的其他名稱取決于平臺和語言構(gòu)造:
- window:是引用全局對象的經(jīng)典方式,但它只適用于普通瀏覽器環(huán)境; 不在 Node.js 和 WebWorkers 中。
- self:在瀏覽器中隨處可用,包括 WebWorkers。但是 Node.js 不支持它。
- global:僅在 Node.js 中可用。
全局對象包含所有內(nèi)置全局變量。
4.全局環(huán)境
全局作用域是“最外層”作用域 - 它沒有外部作用域。它的環(huán)境是全局環(huán)境。每個環(huán)境都通過由外部引用鏈接的一系列環(huán)境與全局環(huán)境相關聯(lián)。全局環(huán)境的外部引用為 null。
全局環(huán)境結(jié)合了兩個環(huán)境記錄:
- 對象式環(huán)境記錄,其作用類似于普通環(huán)境記錄,但保持其綁定與對象同步。在這種情況下,對象是全局對象。
- 聲明式環(huán)境記錄。
下圖顯示了這些數(shù)據(jù)結(jié)構(gòu)。

接下來的兩個小節(jié)將解釋如何組合對象記錄和聲明式記錄。
4.1創(chuàng)建變量
為了創(chuàng)建一個真正全局的變量,你必須處于全局作用域內(nèi) - 必須要在 scripts 的頂層:
- 頂級 const, let 和 class 在聲明式環(huán)境記錄中創(chuàng)建綁定。
- 頂級 var 和函數(shù)聲明在對象式環(huán)境記錄中創(chuàng)建綁定。
< script > const one = 1 ; var two = 2 ; </ script > < script > // All scripts share the same top-level scope: console . log ( one ); // 1 console . log ( two ); // 2 // Not all declarations create properties of the global object: console . log ( window . one ); // undefined console . log ( window . two ); // 2 </ script >
此外,全局對象包含所有內(nèi)置全局變量,并通過對象式記錄將它們給全局環(huán)境。
4.2讀取/設置變量
當我們獲取或設置變量并且兩個環(huán)境記錄都具有該變量的綁定時,聲明式環(huán)境記錄將獲勝:
< script > let foo = 1 ; // 聲明式環(huán)境記錄 globalThis . foo = 2 ; // 對象式環(huán)境記錄 console . log ( foo ); // 1 (聲明式記錄獲勝) console . log ( globalThis . foo ); // 2 </ script >
5.模塊環(huán)境
每個模塊都有自己的環(huán)境,它存儲所有頂級聲明 - 包括導入。模塊環(huán)境的外部環(huán)境是全局環(huán)境。
結(jié)論:為什么JavaScript既有全局變量又有全局對象?
通常認為將全局變量掛載到全局對象上是錯誤的。因此,較新的構(gòu)造(如 const, let 和 classes)會創(chuàng)建正常的全局變量,不會成為全局對象的屬性。(在 script作用域內(nèi)時)。
值得慶幸的是,大多數(shù)用現(xiàn)代 JavaScript 編寫的代碼都存在于 ECMAScript 模塊和 CommonJS模塊中,每個模塊都有自己的作用域。