作用域、作用域链、执行上下文

71 阅读5分钟

前言:

代码执行之前的编译过程: 1、分词/词法分析 将代码分为词法单元,如var a = 2 分为 var 2 = 2 2、解析/语法分析 将词法单元组转为程序语法结构树简称AST 3、代码生成 将AST转为可执行的代码

查找变量有两种概念,lhs和rhs,lhs是赋值操作的左侧变量进行查询,找到变量进行赋值,而右侧的变量则是查找变量的值给左侧赋值(类似引用,值传递)

作用域

是什么???

作用域决定了变量和函数的使用范围,即决定可以在哪里访问变量和函数

代码被定义的时候产生,决定了变量或函数的使用范围,可以通过作用域来编译声明语句判断变量重复声明,编译赋值语句查找是否存在该变量。给变量和函数一个独立的空间,可以互不干扰,不会造成空间污染

作用域是怎么工作的呢?

  • 作用域在编译过程中,如编译var a 时,编译器会询问作用域中是否存在a,如果存在那么会忽略此次声明,也就是在声明变量时,通过作用域可知道是否已经存在相同的变量,从而是否去声明该变量

  • 生成运行时的代码时,代码会处理赋值操作,即a = 2或者b = a,就可以进行lhs或rhs查询作用域中是否存在变量a,存在则进行赋值操作

作用域链

何时创建??

在js引擎  完成  初始化执行上下文环境(完成初始化执行上下文环境是什么时候),就已经确定

如何工作??

嵌套的函数查找属性和对象的时候,是在当前作用域下查找,找不到就找外层作用域,直到全局作用域,由这些作用域形成了作用域链

变量提升和函数提升

js引擎执行代码时候,会先解析语法,进行编译,再去执行,因此在编译的时候会先获取所有声明的变量和函数,这个就叫变量提升和函数提升

  • 变量声明,打印输出是undefined
  • 函数声明,打印输出是这个函数
  • 注意:
  1. 函数提升优先级大于变量提升优先级
  2. 函数声明和函数表达式是不一样
    如:function a() = {},是函数声明
    var a = function(){},是函数表达式
    

预编译四个过程,记住,变量声明的时候,变量值是undefined,也就是变量未赋值,函数声明的时候,如果遇到函数名和变量名相同,那么函数声明会覆盖变量声明 即 console.log(a) var a = 1 function a(){} 因为变量提升和函数提升 ,那么会先打印出function a(){},而不是undefined

执行上下文

类型:

  • 初始化执行上下文
  • 运行执行上下文
  • 结束执行上下文 在执行代码的时候产生,执行函数之前会先进行准备工作,就是创建执行上下文,又称为执行环境

包括:(es5-)

  1. 变量对象
  2. 作用域链
  3. this

包括:(es5+)

  1. 词法环境:

    • 环境记录: 保存着代码块内的声明的变量和函数,(相对应变量对象或者活动对象)
    • 对外部词法环境的引用(outer):
      从而形成了多个词法环境的嵌套结构,来实现可以访问外部环境变量的能力(相对应作用域链)
  2. 环境记录:

    • 声明式环境记录: 声明环境: 绑定变量声明,如var、let、const、class、module、import、function 函数环境: 每个函数作用域和下文环境的具体情况 模块环境: 表示模块的外部范围以及绑定情况
    • 对象式环境记录: 对象的增删改查
    • 全局环境记录: 表示最外层作用域

this的指向

是执行上下文的属性,由函数执行的时候确定,函数被调用时绑定
  • 函数中有this,但是没有被调用,即this前没有点,那么this就是window,严格模式是undefined
  • 函数中有this,this前有点,this被调用,那么点前面是谁,this就是谁
  • 点前面还有点,那么this也只是指向离this最近的点的主体,
  • 在构造函数中被调用,那么this指向构造函数的实例
  • 箭头函数的this继承上一级函数执行上下文的this
  • 回调函数的this指向window
  • call、apply、bind改变this的指向
  1. 默认绑定

    独立函数调用

    var name = '小猪课堂';
    function foo(){
      console.log(this) // Window{}
      console.log(this.name) // 小猪课堂
    }
    foo();
  1. 隐式绑定

例如:

    function foo(name) {
      this.name = name;
    }
    let obj1 = {
      foo: foo
    }
    obj1.foo('小猪课堂'); //foo是在obj1对象中调用,foo调用时,他的执行上下文是obj1,所以this会绑定到obj1中
  1. 显式绑定

call、apply、bind改变this的指向

  1. new绑定
  • 创建一个新对象
  • 新对象继承构造函数的原型
  • 新对象通过apply,调用构造函数,绑定到函数调用的this(不懂这句话),this指向新对象,并且新对象调用构造函数,(使得新对象获取构造函数的属性?)怎么获取构造函数的属性
  • 返回对象

闭包

上级作用域变量的生命周期因为下级作用域的引用而没有被释放,需要等到下级作用域执行完后才能得到正常释放