(译)看得见的 JavaScript: 作用域(链)Scope (Chain)
var code = "8aecffca-8909-4927-a3e0-49a8d64e4bd6"
让我们看一下以下这段代码:
const name = "Lydia"
const age = 21
const city = "San Francisco"
function getPersonInfo() {
const name = "Sarah"
const age = 22
return `${name} is ${age} and lives in ${city}`
}
console.log(getPersonInfo())
我们调用了函数 getPersonInfo
,它将返回一个由 name
、age
和 city
变量组成的字符串:Sarah is 22 and lives in San Francisco
。但是,getPersonInfo
函数里并未包含一个叫做 city
的变量,它是怎么知道 city
的值的呢?
首先,不同的上下文都会被分配不同的内存空间。我们拥有默认的全局上下文(浏览器里是 window
,在 Node 中是 global
),以及一个局部上下文,它是在 getPersonInfo
函数被调用时出现的。每一个上下文都会有一个作用域链。
对于 getPersonInfo
函数,作用域链看起来有点像下面这样(不必担心,暂时可以不用理解它):
作用域链基本上就是一个对象的“引用链条”,它将包含当前执行上下文中所有值的引用。当执行上下文被创建的时候,作用域链就出现了,也就是说,它是在运行时被创建的。
然而,在这篇文章中,我并不打算讨论 activation object (活跃对象或激活对象)或是执行上下文,让我们聚焦作用域!在接下来的例子中,执行上下文中的键值对代表作用域链中变量的引用。
全局执行上下文中的作用域链有一条对 3 个变量的引用:带有值 Lydia
的变量 name
,带有值 21
的变量 age
,以及带有值 San Francisco
的变量 city
。在局部上下文中,我们有一条对 2 个变量的引用:带有值 Sarah
的变量 name
,以及带有值 22
的变量 age
。
当我们试着获取 getPersonInfo
函数中的变量时,引擎首先会检查局部作用域。
局部作用域链中含有一条对 name
和 age
的引用!name
中的值是 Sarah
、age
中的值是 22
。但是现在,当它试着去获取 city
时会发生什么?
为了找到 city
的值,引擎将会顺着作用域链向下找。这基本上意味着引擎不会简单地放弃查找:它会为你努力工作,看看是否可以在局部作用域外部找到变量 city
,这本例中,这个外部作用域就是全局对象(global object)。
在全局上下文中,我们定义了变量 city
,它的值是 San Franciso
,因此就有了对变量 city
的引用。既然这个变量有对应的值,那么函数 getPersonInfo
就能返回出字符串 Sarah is 22 and lives in San Francisco
。
我们沿着作用域链向下找,但是不会反过来向上找。(好吧,这也许会令人很疑惑,因为有些人的说法正好相反,下说成上,因此我要提一句的是:你可以沿着作用域链向外找,而不是向内找……)我喜欢将它想象成瀑布:
甚至更深层次的:
让我们以这段代码为例。
跟之前的例子相比,几乎是一样的,一个比较大的差异是:我们现在只在 getPersonInfo
函数中声明了变量 city
,而不是在全局作用域。我们没有去调用 getPersonInfo
函数,也就没有局部上下文被创建。此时,我们试着在全局上下文中获取变量 name
、age
以及 city
的值。
它将会抛出错误:ReferenceError
!在全局作用域中,它找不到 city
对应的引用值,也没有其他外部的作用域了,它不能再继续往上找了。
这样一来,你就能利用作用域作为一种保护变量的途径,而且可以出现同名变量。
除了全局和局部作用域,还有一个块级作用域。用 let
和 const
关键字声明的变量被限制在最近的大括号({}
)中。
const age = 21
function checkAge() {
if (age < 21) {
const message = "You cannot drink!"
return message
} else {
const message = "You can drink!"
return message
}
}
你可以将作用域想象成这样:
我们有一个全局作用域,一个函数作用域以及两个块级作用域。我们可以声明变量 message
两遍,因为变量的范围在大括号中,彼此独立、互不影响。
快速回顾一下:
- 你可以把作用域链当作一条可以访问到当前上下文中值的引用链条。
- 作用域能够让变量名称重复成为可能。
添加我的微信:enjoy_Mr_cat,共同成长,卷卷群里等你 🤪。