词法环境和函数作用域

72

在JavaScript中,词法环境和作用域是否是同一件事?

3个回答

80

根据我刚从《JavaScript忍者秘籍2/e》中学到的内容,给出答案:

它们是不同的概念但有关联:我们需要定义一个相关概念——执行上下文和其堆栈来理解。

执行上下文执行上下文堆栈执行上下文是内部JavaScript构造,用于跟踪函数或全局代码的执行。 JS引擎维护一个栈数据结构——执行上下文堆栈调用栈,其中包含这些上下文。 全局执行上下文位于此堆栈底部。当函数执行开始时,将创建并推送一个新的执行上下文到堆栈中。特定的执行上下文跟踪相应函数正在执行的语句指针。在对应函数的执行结束时,其执行上下文从堆栈中弹出。

词法环境是内部JS引擎构造,用于保持标识符-变量映射(这里的标识符指变量/函数名称,而变量则是实际对象(包括函数类型对象)或基本值的引用)。词法环境还保存对父级词法环境的引用。

现在,对于每个执行上下文

  1. 将创建一个相应的词法环境,并且
  2. 如果在该执行上下文中创建任何函数,则该函数的内部属性([[Environment]])中会存储对该词法环境的引用。因此,每个函数跟踪与其创建时的执行上下文相关联的词法环境。

每个词法环境都跟踪其父级词法环境(即父级执行上下文的词法环境)。结果,每个函数都有一系列附加到它的词法环境链。

注意:在JS中,函数是对象。使用语句创建函数意味着创建类型为Function的对象。因此,像其他对象一样,函数可以持有内部和用户定义的属性。

作用域是一个与语言无关的概念,指变量或函数对执行代码的可见性。在JS中,如果变量或函数存在于当前词法环境或封闭函数的词法环境链中,则其对执行代码可见。在全局代码的情况下,此链不存在。

希望现在您理解了...

注意:类似于函数,通过在ES6中引入的let和const,当一个块开始执行(if块、for循环块等),也会创建一个新的词法环境,其父函数的词法环境作为父级。


我想知道这个回复是否也适用于ES6(你所提到的书是在ES6发布之前出版的)。 - marco
ES6在2015年6月被定稿,而该书(第二版)在2016年9月出版。 - adnan2nd
1
这对我来说很有趣,[[Environment]]和[[Scope]]内部属性有什么不同? - Murad Sofiyev
4
@MuradSofiyev,没有内部的[[Scope]]属性。虽然在ECMAScript 5.1中提到了它,但是从ECMAScript 6th开始,它被[[Environment]]内部属性替代。因此,我认为它们大部分相同。 - ansavchenco
难道说函数的词法环境跟踪其父级词法环境不是更准确吗?而不是函数跟踪其父级词法环境,因为作用域链的所有魔力都在于词法环境链。 - Nadav Shlush

33

词法环境是函数编写所在的环境。也就是说,它的静态顺序/位置无论从哪里调用,都是固定的。

变量/函数的作用域基本上是变量可见/可访问的位置。

执行上下文是运行时任何时候执行栈的状态。这是当前执行上下文。


2
如果我在看这个答案之前没有读其他的答案,可能会有点困惑,但我认为这个答案最好地概括了理解和区分词法环境和作用域的简单方法。 - Hawkeye

31
这是规范关于词法环境的说法:
词法环境是一种规范类型,用于根据 ECMAScript 代码的词法嵌套结构定义标识符与特定变量和函数之间的关联。词法环境由环境记录和可能为空的外部词法环境引用组成。
基于此,我认为当人们谈论“作用域”时,通常是指这个。
虽然可以争辩说“作用域”实际上被定义为“声明性环境记录”:
每个声明性环境记录都与包含变量和/或函数声明的 ECMAScript 程序作用域相关联。声明性环境记录绑定了其作用域内所包含的声明定义的标识符集合。
如果您将“作用域”视为包含标识符和值之间绑定的东西,则第二个定义可能更适合。如果您将其视为知道其祖先作用域的东西,则第一个定义更合适。

编辑:第三个选项是 "Execution Context"。


第二个定义是指“作用域”,因此不能成为“作用域”本身的定义。 - OrangeDog
可以。问题是关于“编程”范围的。第二个定义使用了“正常”的(如果你愿意,就是字典)英语定义词汇“scope”。 - zenw0lf
1
我认为变量/函数的作用域确实是当前执行上下文的词法环境。请参阅“9.2.4函数初始化”(http://www.ecma-international.org/ecma-262/8.0/#sec-functioninitialize):“* ...由范围指定的词法环境*”。这是一个非常明显的提示。 - Magnus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接