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声明变量时必须初始化)

  • 相关阅读:
    Service Name Port Number Transport Protocol tcp udp 端口号16bit
    linux linux 互传文件 win 不通过 ftp sftp 往linux 传文件(文件夹)
    soft deletion Google SRE 保障数据完整性的手段
    Taylor series
    Taylor's theorem
    Moving average
    REQUEST
    Unix file types
    mysqld.sock
    Tunneling protocol
  • 原文地址:https://www.cnblogs.com/joeynkay/p/13527069.html
Copyright © 2011-2022 走看看