执行上下文与闭包到底是什么?

252 阅读5分钟

什么是执行上下文
  Javascript中我们是一段一段去执行代码,比如全局代码块,函数代码块,eval代码块,我们把一段段代码块,执行时所需的所有信息叫做执行上下文。
​   ES3中执行上下文分为三个部分:
​   1.scope:作用域
​   2.variable object: 变量对象
​   3.this value :this 的值
​   ES5中把其命名方式更改成如下:
​   1.lexical environment词法环境 - 当获取变量的时候使用
​   2.variable environment变量环境 -当变量声明的时候使用
​   3.this value :this 的值
  在ES2018中,执行上下文又变成了这个样子,被归入 lexical environment,但是增加了不少内容:
​   1.lexical environment词法环境 - 当获取变量的时候使用
​   2.variable environment变量环境 -当变量声明的时候使用
​   3. code evaluation state :用于恢复代码的执行位置
​   4.Function:执行的任务是函数时候使用,表示正在执行的函数 ​   5.ScriptOrModule:执行的任务是脚本或者模块的时候使用,表示正在执行的代码
​   6.Realm:使用的基础库和内置对象实例
​   7.仅生成器上下文有这个属性,表示当前的生成器
那么什么是变量对象?
  大多数代码块具有的生命周期分为三个阶段,创建阶段,执行阶段,回收阶段;
  1.在创建阶段,是创建执行上下文的过程(创建变量对象,建立作用域链,以及确定this的指向。
  2.在执行阶段,主要对变量做赋值,函数的进行调用,执行其他代码;
  3.回收阶段,就是等待回收和释放的阶段;
  所谓变量对象是执行上下文中用于存放:函数形参,函数声明,变量声明的数据作用域;
  当我们创建变量对象的时候,首先先解析上下文中函数的形参,把其存在变量对象中,然后会解析函数的声明,把函数的名作为属性名,其属性值为那个被声明的函数;在这个过程中,如果发现函数的名字已经存在,那就会把它覆盖;   然后检查当前上下文中的变量声明,每找到一个变量声明,就在变量对象中以变量名建立一个属性,属性值为undefined。如果发现变量名已经存在,这个过程并不会覆盖它,而是选择去忽略;

console.log(a)
var a = 1;   
function a (a) {
console.log(1)
}

从上面代码分析,在这个代码块的创建阶段,首先创建一个变量对象,按照上文规律,首先解析函数的形参(undefined),然后解析函数声明(fn a ()),最后解析变量声明(undefined但不覆盖) 然后执行代码,先打印a 的值,然后把a赋值为1;这就是我们所说的变量提升的原理

什么是this?
   this是JavaScript中的一个关键字,它的使用方法类似于一个变量;其是一个执行上下文中的一个重要的组成部分,同一个函数的调用方法不同   得到的this也会不同;
  普通函数的this是由调用它所使用的引用而决定,其原理在于我们获得函数的表达式,它实际上返回的并非函数的本身,而是一个Reference类型;
  Reference类型由两个部分组成:一个对象和一个属性值,那个对象其实就是我们所说的this;比如:
```

      function fn () {
      console.log(this)
      }
      let obj = {
       fn
      }
      obj.fn()
      ```

  在上面的代码中,obj就是Reference中的对象,fn()就是那个属性值,所以我们可以非常】肯定的知道:调用函数时使用的引用,决定了函数执行时刻的this值;   从原理上看Javascript定义了一个复杂的机制[[thisMode]]私有属性。其有三个取值:
  1.lexical: 表示从上下文中寻找this;(箭头函数)
  2.global:表示当this 取值 undefined 的时候,取的是全局对象;(普通函数)
  3.strict:严格模式中使用,可能为null和undefined;(class中的方法)
  函数创建新的执行上下文中的词法环境记录时,会根据[[thisMode]]来标记新纪录的[[thisBindingStatus]]私有属性。 当代码执行遇到this时,会逐层检查当前词法环境中的[[thisBindingStatus]],当找到有this的环境记录的时候获取到this的值;

闭包到底是什么?
​   闭包是一个从开始学Javascript就开始接触的一个词汇,但是闭包到底是什么,JavaScript标准中并没有明确的说明,其实闭包在计算机领域有三个不同的定义:
​   1.编译原理:它是处理语法产生时的一个步骤
​   2.在计算几何中:它表示包裹平面点集的凸多边形
​   3.在编程语言领域:它代表着一种函数
​   我们每天在说的闭包,指的大多都是第三种定义,这个概念第一次出现在1964年的《The Computer Journal》上,那里面把闭包定义为:"带有一系列信息的表达式" 。所以我们可以这样去理解闭包:所谓闭包只是一个绑定了作用域的函数;它与普通函数的唯一区别是它绑定了它的作用域;
​   在古典定义中,闭包包括两个部分:
​   1.环境部分(包括环境和标识符列表)
​   2.表达式部分
​   但是我们把目光切回到JavaScript,JavaScript中的函数完全符合闭包的定义,它的环境部分是其词法作用环境,它的表示符是函数中未曾声明过的变量,它的表达式部分是函数体;所以由于上述的分析,闭包并不是所谓的它在JavaScrip执行上下文或者作用域的概念,其实中对应的概念就是JavaScript中的函数;