本系列
正文
现在开始讲作用域链🕺🏼在这篇文章中,我假设您了解执行上下文:尽管如此,我也将很快就此发表一篇文章😃
让我们看下面的代码:
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用大括号括起来,因此我们能够对变量进行两次声明。
快速回顾:
- 您可以将“作用域链”视为对我们可以在当前上下文中访问的值的引用链。
- 作用域可以让我们重复使用外部作用域里的变量
范围(链)就是这样!关于这一点,还有很多话要说,所以我会在空闲时间添加一些额外的信息。如果您遇到任何困难,请随时提出问题,我很乐意提供帮助!💕