JavaScript 的全局变量如何真正地工作
在这篇博客文章中,我们调查 JavaScript 的全局变量如何工作。几个有趣的想象在发生:脚本作用域、被称作全局对象以及其它更多。
作用域
变量的词法作用域(简称:作用域)是可以访问它的区域。JavaScript 的作用域是静态的(它们在运行时不会变化)并且可以嵌套,举例来说:
function func() {
// (A)
const foo = 1
if (true) {
// (B)
const bar = 2
}
}
if
声明(line B)的作用域引用嵌套在函数 func()
(line A) 的内部作用域。
作用域 S 最深的作用域是作用域 S 的外部作用域。在你之中,func
是 if
的外部作用域。
词法环境
在 JavaScript 的语言规范中,作用域是通过词法环境(lexical environments)来执行的。它们由两部分组成:
- 将变量名称映射到变量值的环境记录(思考字典)。这是 JavaScript 存储变量的地方。环境记录中的一个键值条目称为绑定。
- 外部环境的引用 - 表示当前环境所代表的作用域的外部作用域的环境。
于是嵌套作用域树由嵌套的环境树声明,外部引用链接在一起。
全局对象
全局对象是指属性是全局变量的对象。我们很快就会检查它是如何适应环境树的,它有几个不同的名称:
- 存在任何位置(功能提案):
globalThis
- 全局对象的其它名称取决于平台和语言构造:
window
: 是全局对象的经典引用。但是它仅仅运行在一般浏览器代码中;不能在 Node.js 和 Web Workers 中使用。self
: 在浏览器中随处可用,包括在 Web Workers。但是 Node.js 没有提供支持。global
: 仅仅在 Node.js 中可用。
全局对象包含所有的内置全局变量。
全局环境
全局作用域是最外层的作用域 - 它没有上层作用域。它的环境是全局环境(global environment)。每个环境都通过由外部引用链接的一系列环境与全局环境相关联。全局环境的外部引用是 null
。
全局环境联合了两个环境记录:
- 一个对象环境记录工作像一个通用的环境记录,但与对象保持其绑定同步。在这种情况下,对象是全局对象。
- 普通(声明性)环境记录。
下图显示了这些数据结构。脚本作用域和模块环境很简单地解释。
接下来的两个小节将解释如何组合对象记录和声明性记录。
创建变量
为了创建一个真正全局的变量,必须处于全局作用域内 - 这只是顶层脚本的情况:
- 顶级
const
、let
和class
建立在声明记录绑定。 - 顶级
var
和函数声明在对象记录中创建绑定。
<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>
另外,全局对象包含所有内置全局变量,并通过对象记录将它们贡献给全局环境。
获取或设置变量
当我们获取或设置变量并且两者环境记录都具有该变量的绑定时,声明性记录将获胜:
<script>
let foo = 1 // 声明环境记录
globalThis.foo = 2 // 对象环境记录
console.log(foo) // 1 (声明记录获胜)
console.log(globalThis.foo) // 2
</script>
模块环境
每个模块都有它自己的环境上下文。它存储所有的顶级声明 - 包含导入的(imports)。模块环境的外部环境是全局环境。
总结:为什么 JavaScript 拥有通用全局变量和全局对象两者?
通常认为全局对象是一种误解。因此,较新的构造(如 const
,let
和类)会创建正常的全局变量(在脚本作用域内)。
值得庆幸的是,大多数用现代 JavaScript 编写的代码都存在于 ECMAScript 模块和 CommonJS 模块中。每个模块都有自己的作用域,这就是为什么管理全局变量的规则很少对基于模块的代码很重要。
更多阅读
- ECMAScript 规范中的 “Lexical Environments” 提供一个通用环境概述。
- 全局环境 “Global Environment Records”
文章由 吳文俊 翻译,原文地址 How do JavaScript’s global variables really work?,转载请注明来源。