You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A Lexical Environment is a specification type used to define the association of Identifiers to specific variables and functions based upon the lexical nesting structure of ECMAScript code. A Lexical Environment consists of an Environment Record and a possibly null reference to an outer Lexical Environment.
第一步 词法分析 :V8 刚拿到执行上下文的时候,会把代码从上到下一行一行的进行分词/词法分析(Tokenizing/Lexing),例如 var a = 1; ,会被分成 var 、 a 、 1 、 ; 这样的原子符号((atomic token)。词法分析=指登记变量声明+函数声明+函数声明的形参。
前面说过,词法作用域也叫静态作用域,变量在词法阶段确定,也就是定义时确定。虽然在 bar 内调用,但由于 foo 是闭包函数,即使它在自己定义的词法作用域以外的地方执行,它也一直保持着自己的作用域。所谓闭包函数,即这个函数封闭了它自己的定义时的环境,形成了一个闭包,所以 foo 并不会从 bar 中寻找变量,这就是静态作用域的特点。
词法环境(Lexical Environment)
官方定义
官方 ES2020 这样定义词法环境(Lexical Environment):
词法环境是一种规范类型(specification type),它基于 ECMAScript 代码的词法嵌套结构,来定义标识符与特定变量和函数的关联关系。词法环境由环境记录(environment record)和可能为空引用(null)的外部词法环境组成。
说的很详细,可是很难理解喃🤔
下面,我们通过一个 V8 中 JS 的编译过程来更加直观的解释。
V8 中 JS 的编译过程来更加直观的解释
大致分为三个步骤:
var a = 1;
,会被分成var
、a
、1
、;
这样的原子符号((atomic token)。词法分析=指登记变量声明+函数声明+函数声明的形参。在第一步中,我们看到有词法分析,它用来登记变量声明、函数声明以及函数声明的形参,后续代码执行的时候就可以知道要从哪里去获取变量值与函数。这个登记的地方就是词法环境。
词法环境包含两部分:
每个环境能访问到的标识符集合,我们称之为“作用域”。我们将作用域一层一层嵌套,形成了“作用域链”。
词法环境有两种 类型 :
this
的值指向这个全局对象。arguments
对象。对外部环境的引用可以是全局环境,也可以是包含内部函数的外部函数环境。环境记录 同样有两种类型:
如果用伪代码的形式表示,词法环境是这样哒:
例如:
对应的执行上下文、词法环境:
词法环境与我们自己写的代码结构相对应,也就是我们自己代码写成什么样子,词法环境就是什么样子。词法环境是在代码定义的时候决定的,跟代码在哪里调用没有关系。所以说 JS 采用的是词法作用域(静态作用域),即它在代码写好之后就被静态决定了它的作用域。
静态作用域 vs 动态作用域
动态作用域是基于栈结构,局部变量与函数参数都存储在栈中,所以,变量的值是由代码运行时当前栈的栈顶执行上下文决定的。而静态作用域是指变量创建时就决定了它的值,源代码的位置决定了变量的值。
在此例中,静态作用域与动态作用域的执行结构可能是不一致的,
bar
本质上就是执行foo
函数,如果是静态作用域的话,bar
函数中的变量x
是在foo
函数创建的时候就确定了,也就是说变量x
一直为1
,两次输出应该都是2
。而动态作用域则根据运行时的x
值而返回不同的结果。所以说,动态作用域经常会带来不确定性,它不能确定变量的值到底是来自哪个作用域的。
大多数现在程序设计语言都是采用静态作用域规则,如C/C++、C#、Python、Java、JavaScript等,采用动态作用域的语言有Emacs Lisp、Common Lisp(兼有静态作用域)、Perl(兼有静态作用域)。C/C++的宏中用到的名字,也是动态作用域。
词法环境与闭包
也就是说,闭包是由 函数 以及声明该函数的 词法环境 组合而成的
基于我们对词法环境的理解,上述例子可以抽象为如下伪代码:
前面说过,词法作用域也叫静态作用域,变量在词法阶段确定,也就是定义时确定。虽然在
bar
内调用,但由于foo
是闭包函数,即使它在自己定义的词法作用域以外的地方执行,它也一直保持着自己的作用域。所谓闭包函数,即这个函数封闭了它自己的定义时的环境,形成了一个闭包,所以foo
并不会从bar
中寻找变量,这就是静态作用域的特点。为了实现闭包,我们不能用动态作用域的动态堆栈来存储变量。如果是这样,当函数返回时,变量就必须出栈,而不再存在,这与最初闭包的定义是矛盾的。事实上,外部环境的闭包数据被存在了“堆”中,这样才使得即使函数返回之后内部的变量仍然一直存在(即使它的执行上下文也已经出栈)。
The text was updated successfully, but these errors were encountered: