原文: JavaScript Visualized(7 Part Series): 3. Scope (Chain) - By Lydia Hallie
学习JS的作用域时间🕺🏼这篇文章假设你已经对执行上下文有一定了解:我也会尽快写一篇关于它的文章😃
看看下面这段代码:
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或者上下文,让我们聚焦于作用域。在下面的例子中,上下文中的键值对代表作用域链对变量的引用。
全局上下文的作用域链有一个指向三个变量的引用:值为Lydia的name,值为21的age和值为San Francisco的city。局部上下文的作用域链有一个指向两个变量的引用:值为Sarah的name和值为22的age。
当我们试图在getPersonInfo函数中访问变量时,js引擎首先会在局部作用域链中查找。
局部作用域链中能找到name和age!,值分别为Sarah和22。但是访问city的时候发生了什么?
为了找到city的值,引擎顺着作用域链往下找。也就是说JS引擎不会这么容易就放弃:它会努力为你去当前作用域指向的更外层的作用域继续找city变量,在本例中就是全局对象。
在全局上下文中,我们声明过变量city = 'San Francisco',因此能找到city。现在我们找到了所有变量的值,getPersonInfo函数就返回了字符串Sarah is 22 and lives in San Francisco🎉
我们可以顺着作用域链往下找,但是我们不能反方向逆着作用域链往上找。(好吧这可能会让人迷惑,因为有人会说这是往上找而不是往下,所以我在这里重新解释:你可以去到更外层的作用域,而不是更里面...(更更里面...?)的作用域)。我喜欢把它想象成一种瀑布:
或者更深更里面:
让我们以下面的代码作为例子:
和之前的例子基本一样,只有一个明显的差别:这次我们只在getPersonInfo函数内部定义了city,而不是在全局作用域下。我们没调用getPersonInfo函数,所以现在没有创建任何局部上下文。然后,我们在全局上下文中获取name, age和city的值。
程序抛出了一个Reference Error!在全局作用域中找不到city变量,并且也没有更外层的作用域了,也就没法顺着作用域链继续找了。
你可以用这种方式通过作用域来“保护”你的变量并且复用变量名。
除了全局和局部作用域,JS中还有块级作用域。例如通过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变量两次就是因为它们分属于不同的花括号中。
快速回顾下:
- 可以把“作用域”链看成是在当前上下文下对所有能访问到的值的引用链;
- 作用域让变量名可以在作用域链的更外层复用,毕竟访问时只能顺着作用域链往下找,而不是往上找(所以不用担心覆盖);
这就是作用域(链)的全部内容了!还有很多很多要阐述的所以当我有空的时候可能会添加其他的内容。有任何疑问都可以来问我,我很乐意提供帮助!💕