全部 / 前端 / 技术 · 2023年3月21日 0

JavaScript 可视化:作用域(链)

原文地址: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 的值为 Lydiaage 的值为 21city 的值为 San Francisco 。在本地上下文中,我们引用了 2 个变量: name 的值为 Sarahage 的值为 22

当我们尝试访问 getPersonInfo 函数中的变量时,引擎首先检查局部作用域链。

1

本地作用域链引用了 nameagename 的值为 Sarahage 的值为 22 。但是现在,当它尝试访问 city 时会发生什么?

为了找到city的值,引擎 "沿着作用域链向下走"。这基本上意味着引擎不会轻易放弃:它为你努力工作,看是否能在本地作用域有一个引用的外部作用域中找到变量city的值,在这个例子中就是全局对象。

2

在全局上下文中,我们声明变量 city 的值为 San Francisco ,因此引用了变量 city 。现在我们有了变量的值,函数 getPersonInfo 可以返回字符串 Sarah is 22 and lives in San Francisco 🎉

我们可以沿着作用域链往下走,但我们不能沿着作用域链往上走。 (好吧,这可能会让人感到困惑,因为有些人说向上而不是向下,所以我改一下:你可以去外部的作用域,但不能去更内部的……(内部的……?)作用域。我喜欢把这想象成一种瀑布。

甚至更深:

我们以这段代码为例。

它几乎是一样的,但是有一个很大的不同:我们现在只在 getPersonInfo 函数中声明了 city ,而不是在全局范围内。我们没有调用 getPersonInfo 函数,因此也没有创建本地上下文。然而,我们尝试在全局上下文中访问 nameagecity 的值。

3

它抛出一个 ReferenceError !它无法在全局作用域中找到对名为 city 的变量的引用,并且没有要查找的外部作用域,它也无法在作用域链中向上移动。

这样,您可以使用作用域作为一种“保护”您的变量并重复使用变量名称的方法。

除了全局和局部作用域之外,还有一个块作用域。使用 letconst 关键字声明的变量的范围限定为最近的大括号 ( { } )。

const age = 21

function checkAge() {
  if (age < 21) {
    const message = "You cannot drink!"
    return message
  } else {
    const message = "You can drink!"
    return message
  }
} 

您可以将范围可视化为:

我们有一个全局作用域、一个函数作用域和两个块级作用域。我们能够两次声明变量 message ,因为变量的范围限定在大括号内。

快速回顾一下:

  1. 您可以将“作用域链”视为对我们可以在当前上下文中访问的值的引用链。
  2. 作用域还使得我们可以重用在作用域链中较下方定义的变量名,因为作用域链只能向下访问,而不能向上。