ECMAScript学习:执行上下文的创建、入栈及出栈

2018-11-2908:33:10WEB前端开发Comments2,381 views字数 2804阅读模式

一、执行上下文(Exexution Contexts)

执行上下文(Exexution Contexts):用来通过ECMAScript编译器来追踪代码运行时计算的一种规范策略。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/8183.html

可以将执行上下文理解为伴随代码解析运行始终并记录其状态的一个抽象装置。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/8183.html

ExecutionContext = {
    VariableEnvironment: { ... },
    LexicalEnvironment: { ... },
}
复制代码

执行上下文同时包含变量环境组件(VariableEnvironment)和词法环境组件(LexicalEnvironment),这两个组件都属于我们之前所说的词法环境(Lexical Environment)。为什么存在两个环境组件,我们稍后将进行详细讨论文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/8183.html

二、执行上下文栈

执行上下文栈(Execution Context Stack):是一个后进先出的栈式结构(LIFO),用来跟踪维护执行上下文。运行执行上下文(running execution context) 始终是执行上下文栈的顶层元素。那么什么时候会创建新的执行上下文呢?文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/8183.html

ECMAScript可执行代码有四种类型:全局代码,函数代码,模块代码和eval。每当从与当前运行执行上下文相关联的可执行代码转到与此执行上下文不相关的可执行代码时,会创建新的执行上下文,将其压入执行上下文栈并成为正在运行的执行上下文。当相关代码执行完毕返回后,将正在运行的执行上下文从执行上下文栈删除,之前的执行上下文又成为了正在运行的执行上下文。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/8183.html

我们通过一个动图来看一下执行上下文栈的工作过程文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/8183.html

ECMAScript学习:执行上下文的创建、入栈及出栈
  1. 开始执行任何JavaScript代码前,会创建全局上下文并压入栈,所以全局上下文一直在栈底。
  2. 每次调用函数都会创建新的执行上下文(即便在函数内部调用自身),并压入栈。
  3. 函数执行完毕返回,其执行上下文出栈。
  4. 所有代码运行完毕,执行上下文栈只剩全局执行上下文。

三、执行上下文的创建、入栈及出栈

上面提到过ECMAScript可执行代码有四种类型:全局代码,函数代码,模块代码和eval文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/8183.html

首先要说明的是,这里虽然说是全局代码,但是JavaScript引擎其实是按照script标签来解析执行的,也就是说script标签按照它们出现的顺序解析执行,这也就是为什么我们平时要将项目依赖js库放在前面引入的原因。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/8183.html

JavaScript引擎是按可执行代码块来执行代码的,在任意的JavaScript可执行代码被执行时,执行步骤可按如下理解:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/8183.html

  1. 创建一个新的执行上下文
  2. 设置该执行上下文的变量环境组件(VariableEnvironment)和词法环境组件(LexicalEnvironment)
  3. 将该执行上下文推入执行上下文栈并成为正在运行的执行上下文
  4. 对代码块内的标识符进行实例化及初始化
  5. 运行代码
  6. 运行完毕后执行上下文出栈

变量提升(Hoisting)及暂时性死区(temporal dead zone,TDZ)

我们平常所说的变量提升就发生在上述执行步骤的第四步,对代码块内的标识符进行实例化及初始化的具体表现如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/8183.html

  1. 执行代码块内的letconstclass声明的标识符合集记录为lexNames
  2. 执行代码块内的varfunction声明的标识符合集记录为varNames
  3. 如果lexNames内的任何标识符在varNameslexNames内出现过,则报错SyntaxError,这就是为什么可以用varfunction声明多个同名变量,但是不能用letconstclass声明多个同名变量。
  4. varNames内的var声明的标识符实例化并初始化赋值undefined,如果有同名标识符则跳过

    这就是所谓的变量提升,我们用var声明的变量,在声明位置之前访问并不会报错,而是返回undefined文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/8183.html

  5. lexNames内的标识符实例化,但并不会进行初始化,在运行至其声明处代码时才会进行初始化,在初始化前访问都会报错。

    这就是我们所说的暂时性死区letconstclass声明的变量其实也提升了,只不过没有被初始化,初始化之前不可访问。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/8183.html

  6. 最后将varNames内的函数声明实例化并初始化赋值对应的函数体,如果有同名函数声明,则前面的都会忽略,只有最后一个声明的函数会被初始化赋值。

    函数声明会被直接赋值,所有我们在函数声明位置之前也可以调用函数。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/8183.html

四、为什么需要两个环境组件

首先明确这两个环境组件的作用,变量环境组件(VariableEnvironment)用于记录var声明的绑定,词法环境组件(LexicalEnvironment)用于记录其他声明的绑定(如letconstclass等)。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/8183.html

一般情况下一个Exexution Contexts内的VariableEnvironmentLexicalEnvironment指向同一个词法环境,之所以要区分两个组件,主要是为了实现块级作用域的同时不影响var声明及函数声明。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/8183.html

众所周知,ES6之前并没有块级作用域的概念,但是ES6及之后我们可以通过新增的letconst命令来实现块级作用域,并且不影响var声明的变量,那么这是怎么实现的呢?文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/8183.html

  1. 首先在一个正在运行的执行上下文(running Execution Context)内,词法环境由VariableEnvironmentLexicalEnvironment构成,此执行上下文内的所有标识符的绑定都记录在两个组件的环境记录内。
  2. 当运行至块级代码时,会将LexicalEnvironment记录下来,我们将其记录为oldEnv
  3. 然后创建一个新的LexicalEnvironment(外部词法环境outer指向oldEnv),我们将其记录为newEnv,并将newEnv设置为running Execution ContextLexicalEnvironment
  4. 然后块级代码内的letconst等声明就会绑定在这个newEnv上面,但是var声明和函数声明还是绑定在原来的VariableEnvironment上面。

    块级代码内的函数声明会被当做var声明,会被提升至外部环境,块级代码运行前其值为初始值undefined文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/8183.html

    console.log(foo) // 输出:undefined
    {
        function foo() {console.log('hello')}
    }
    console.log(foo) // 输出: ƒ foo() {console.log('hello')}
    复制代码
  5. 块级代码运行完毕后,又将oldEnv还原为running Execution ContextLexicalEnvironment

目前包括块级代码(在一对大括号内的代码)、for循环语句、switch语句、TryCatch语句中的catch从句以及with语句(with语句创建的新环境为对象式环境,其他皆为声明式环境)都是这样来实现块级作用域的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/8183.html

作者:Logan70
来源:掘金文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/8183.html

  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/gcs/8183.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定