原文地址:https://dev.to/lydiahallie/javascript-visualized-scope-chain-13pd
是时候谈一谈作用域链了🕺🏼 在这篇文章中,我假设您了解一些执行上下文的基础知识:不过我很快也会写一篇关于它的文章😃
让我们看一下下面的代码:
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
函数,作用域链看起来像这样(别担心,它现在还没有意义):
作用域链基本上是一个对象的 "引用链",它包含对该执行上下文中可引用的值(和其他作用域)的引用。(⛓: "嘿,这些是你可以从这个上下文中引用的所有值")。当执行上下文被创建时,作用域链就被创建了,也就是说,它是在运行时被创建的。
然而,在本篇文章中我不会谈论活动对象或执行上下文的概念,我们只需要关注作用域!在以下示例中,执行上下文中的键/值对表示作用域链对变量的引用。
全局执行上下文的作用域链引用了 3 个变量: name
的值为 Lydia
, age
的值为 21
, city
的值为 San Francisco
。在本地上下文中,我们引用了 2 个变量: name
的值为 Sarah
, age
的值为 22
。
当我们尝试访问 getPersonInfo
函数中的变量时,引擎首先检查局部作用域链。
本地作用域链引用了 name
和 age
! name
的值为 Sarah
而 age
的值为 22
。但是现在,当它尝试访问 city
时会发生什么?
为了找到city的值,引擎 "沿着作用域链向下走"。这基本上意味着引擎不会轻易放弃:它为你努力工作,看是否能在本地作用域有一个引用的外部作用域中找到变量city的值,在这个例子中就是全局对象。
在全局上下文中,我们声明变量 city
的值为 San Francisco
,因此引用了变量 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
,因为变量的范围限定在大括号内。
快速回顾一下:
- 您可以将“作用域链”视为对我们可以在当前上下文中访问的值的引用链。
- 作用域还使得我们可以重用在作用域链中较下方定义的变量名,因为作用域链只能向下访问,而不能向上。