zoukankan      html  css  js  c++  java
  • js中的执行上下文--声明提升,暂时性死区等

    在JavaScript代码开始执行前,js解析器做好了一系列的准备工作,并且规定了变量、函数的访问路径。(这个为个人的理解,如有理解错误,请大佬们指正!)
    JavaScript在代码运行前做了什么?会创建执行上下文,也就是准备好代码运行时的环境,包括变量对象、作用域链、this
    代码运行阶段,变量、函数、this的访问,均是从其执行上下文中获取。其中变量的获取分为:执行上下文创建阶段变量初始化的获取,和运行阶段变量赋值后的获取。函数和this在创建阶段已经绑定了引用。

    一、执行上下文

    在js代码执行前,会创建执行上下文,也就是所谓的代码运行环境。那么该运行环境有什么作用?————实际上,代码运行时,关于变量的访问、函数的调用及this的指向绑定,都和执行上下文有着密切的关系,可以认为,都是从执行上下文中获得它们的值。

    1.1运行环境

    JavaScript代码有三种运行环境,对应着三种执行上下文

    • global code:全局作用域下的代码。不包括任何function体内的代码
    • Function Code:函数调用执行的代码,不包括其自身内部的函数的代码
    • Eval Code:eval()执行的代码

    1.2EC三个属性

    执行上下文是一个对象,它具有三个属性:scope chain,variable object,this

    • 变量对象(variable object):vars、function declarations,arguments...
    • 作用域链(scope chain):variable object + all parent scopes
    • this:context object
    1.2.1变量对象variable object

    ​变量对象是与执行上下文相关的数据作用域。存储了在上下文定义的变量和函数声明。

    • variable declaration,初始化为undefined
    • Function declaration,保存函数名称并持有函数体的引用
    • 函数的形参,初始化为undefined
    • 函数执行上下文中,没有使用var声明的变量为全局变量,所以不在变量对象中
    • 函数表达式也不包含在变量对象中
    • let,const声明的变量初始化为uninitialed
    1.2.2作用域链scope chain

    ​全局执行上下文没有外部的作用域,因此定义其作用域链为自身的变量对象。

    ​函数执行上下文中的作用域链:实际上,当函数定义的时候,函数所有的外层变量对象(即集合)都会保存在函数的内部属性[[scope]]中,当创建函数执行上下文时,首先创建了该对象的属性--作用域链,并把[[scope]]复制到scope chain中,但这并不是完整的作用域链。接着便是变量对象variable object的创建,创建过程见下文——JavaScript代码的执行前后做了什么--执行上下文创建的完整过程。创建完成后,把变量对象复制到scope chain的顶端,形成完整的作用域链。

    1.2.3this

    this在创建执行上下文时进行绑定

    1.2.3.1全局代码中的this

    this的绑定指向始终为window对象

    console.log(this)//window,非严格模式下
    //严格模式下为undefined
    
    1.2.3.2函数代码的this
    • 默认绑定:函数调用时,若无前缀,则this绑定为window

      function test() {
          console.log(this)
      }
      test()//window
      
      function f1(){
          function f2(){
              console.log(this)
          }
          f2()
      }
      f1()//window
      
    • 隐式绑定:函数调用时,前面存在调用它的对象,则this会隐式绑定到这个对象上

      function f1(){
          console.log(this.name,this)
      }
      f1()//window
      let obj = {
          name:'hello',
          fn:f1
      }
      obj.fn()//
      
    • 显式绑定:call,apply,bind改变this

    • new绑定:

      new一个函数,实际进行了的操作:

      • 创建一个空对象,将它的引用赋给this,继承函数的原型
      • 通过this将属性和方法添加到这个对象
      • 若没有手动返回其他的对象,则最后返回this指向的对象(即实例)
      • 以构造器的prototype属性为原型创建一个对象
      • 将这个对象和调用参数传给构造器执行,apply。。
      • 如果构造器没有手动返回对象,则返回第一步的对象

      这个实例中的方法中(即对象的方法内的代码中)的this指向所创建的实例

    • 箭头函数中的this

      ​ 箭头函数没有自己的this,this指向取决于外层作用域中的this,一旦箭头函数的this绑定成功,也无法被再次修改,但可以修改外层函数的this指向达到间接修改箭头函数this的目的。
      题外话:定时器中的回调函数,如果是匿名函数,那么this指向window对象,可以改用箭头函数,那么this则默认指向上层作用域中的this,

    二.JavaScript代码的执行前后做了什么----EC的创建过程

    当一段JavaScript代码执行的时候,JavaScript解释器会创建执行上下文,包含了两个阶段:

    • 创建阶段(函数被调用,但在开始执行函数内代码之前)
      • 创建scope chain:复制函数属性[[scope]]scope chain,在变量对象创建完后,将其添加到scope chain的前端,形成完整的作用域链
      • 创建VO/AO
        • 根据函数的参数,创建并初始化arguments object
        • 扫描函数内部代码,查找函数声明
          • 对于所有找到的函数声明,将函数名和函数引用存入到VO/AO
          • 如果VO/AO中已有同名的函数,那么就进行覆盖
        • 扫描函数内部代码,查找变量声明
          • 对于所有找到的var变量声明,都存入到VO/AO中,并初始化为undefined
          • let,const变量声明,初始化为uninitialed
          • 如果变量名称和已经声明的形式参数或函数相同,那么变量声明将不起作用,保留后者,也就是说后者不会受前者声明的干扰
      • 设置this的值
    • 激活/代码执行阶段
      • 设置变量的值、函数的引用,解释/执行代码

    三、结合执行上下文,解释变量及函数的访问规则--声明提升

    js代码开始执行时,浏览器便会创建全局执行上下文(详见EC的创建过程),每有一次函数调用,则创建一次函数执行上下文。
    进行变量、函数及this值的访问时,都会在当前执行上下文中的作用域链进行访问。

    声明提升:

    console.log(var1);//undefined
    var var1 = 1;
    console.log(var1);//1
    
    console.log(fn);
    function fn(){
          console.log('1111');
    }
    

    解析:访问时,都是从执行上下文中的作用域链进行取值,创建执行上下文时,var声明的变量被初始化为undefined,因此在变量被赋值前进行访问,打印了undefined,赋值后访问,打印1;
    函数的声明提升同理,只不过创建执行上下文时,函数的标识符初始化为函数体的引用,故打印的为函数体。

    let,const的暂时性死区:声明前不能进行访问

    console.log(a);
    let a = 1;
    let b;
    console.log(b)
    

    解析:创建执行上下文时,被初始化为uninitialed,访问uninitialed值会抛出错误,而赋值后,则可以访问let,const声明的变量。(代码运行到let声明语句时,若没有进行赋值操作,则默认赋值为undefined,const声明变量时必须初始化)

  • 相关阅读:
    ArcGIS Server 10.1 错误 service failed to start,
    ArcGIS AddIN开发异常之--“ValidateAddInXMLTask”任务意外失败
    关于程序批量处理人工做的数据。。。。
    ArcEngine创建要素类_线类型
    ArcEngine创建要素类01
    ArcEngine创建要素集
    其他人博客地址
    ArcEngine创建要素类_面类型
    ArcEngine判断要素集是否存在
    Arcengine 判断Dataset是否存在
  • 原文地址:https://www.cnblogs.com/joeynkay/p/13527069.html
Copyright © 2011-2022 走看看