7.执行上下文和作用域链。


总结:

  • 1.函数执行上下文:代码运行时在函数执行前创建的执行环境,确定函数代码块中声明的变量和函数,作用域链,this的指向。
  • 2.作用域链:查找变量时,先从当前上下文的变量对象中查找,没有就从父级执行上下文的变量对象中查找,一直找到全局上下文的变量对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链。
  • 3.变量对象构建的过程中会触发变量和函数的声明提升。执行上下文创建阶段会进行变量和函数的初始化声明,变量统一定义为 undefined 需要等到赋值时才会有确定值,而函数则会直接定义。函数声明提升大于变量声明提升。
  • 4.函数能够访问到的上层作用域,在函数创建的时所有的父变量对象就已经被确定且保存在函数的 [[scope]] 属性里。

1.执行上下文定义:当前代码的执行环境。

2.ES3中有三种执行上下文类型:

  • 2-1.全局执行上下文,存在于执行堆栈的最底部脚本的生命周期内不会被栈弹出销毁。全局上下文会生成一个全局对象(浏览器环境全局对象是 window),并且将 this 值绑定到这个全局对象上。
  • 2-2.函数执行上下文,每当一个函数被调用时,都会创建一个新的函数执行上下文(不管这个函数是不是被重复调用的)。
  • 2.3.eval执行上下文,少用不记录。

3.ES3执行上下文包括:

  • 3-1.变量对象VO:每个执行环境文都有一个表示变量的对象,变量对象,全局执行环境的变量对象始终存在,而函数这样局部环境的变量,只会在函数执行的过程中存在。只有函数声明会被加入到变量对象中,而函数表达式则不会:
function a(){}
var a = function _a(){};
console.log(typeof a); // "function" // 函数声明
console.log(typeof _a); // "undefined"  // 函数表达式
  • 3-2.活动对象AO:函数进入执行阶段时,原本不能访问的变量对象被激活成为一个活动对象,自此,我们可以访问到其中的各种属性。其实变量对象和活动对象是一个东西,只不过处于不同的状态和阶段而已。
  • 3-3.作用域链:当查找变量的时候,会先从当前上下文的变量对象中查找,如果没有找到,就会从父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链。
  • 3-4.调用者信息(this):如果当前函数被作为对象方法调用或使用 bind call apply 等 API 进行委托调用,则将当前代码块的调用者信息(this值)存入当前执行上下文,否则默认为全局对象调用。

4.全局执行上下文和函数执行上下文中的变量对象还略有不同,它们之间的差别简单来说:

  • 4-1.全局上下文中的变量对象就是全局对象,以浏览器环境来说,就是 window 对象。
  • 4-2.函数执行上下文中的变量对象内部定义的属性,是不能被直接访问的,只有当函数被调用时,变量对象(VO)被激活为活动对象(AO)时,我们才能访问到其中的属性和方法。

5.作用域链形成:

  • 5-1.函数的作用域在函数创建时就已经确定了。当函数创建时,会有一个名为 [[scope]] 的内部属性保存所有父变量对象到其中。当函数执行时,会创建一个执行环境,然后通过复制函数的 [[scope]] 属性中的对象构建起执行环境的作用域链,然后变量对象 VO 被激活生成 AO 并添加到作用域链的前端,完整作用域链创建完成:
Scope = [AO].concat([[Scope]]);

6.执行上下文数据结构模型:

executionContext:{
  [variable object | activation object]:{
    arguments,
    variables: [...],
    funcions: [...]
  },
  scope chain: variable object + all parents scopes
  thisValue: context object
}

7.执行上下文的生命周期:

  • 7-1.创建阶段:
    • ①在函数被调用时且在具体的函数代码运行之前,JS 引擎会用当前函数的参数列表(arguments)初始化一个“变量对象”并将当前执行上下文与之关联,函数代码块中声明的变量和函数将作为属性添加到这个变量对象上。在这一阶段,会进行变量和函数的初始化声明,变量统一定义为 undefined 需要等到赋值时才会有确定值,而函数则会直接定义。
    • ②构建作用域链。
    • ③确定this的值。
  • 7-2.执行阶段:执行阶段中,JS 代码开始逐条执行,在这个阶段,JS 引擎开始对定义的变量赋值、开始顺着作用域链访问变量、如果内部有函数调用就创建一个新的执行上下文压入执行栈并把控制权交出……
  • 7-3.销毁阶段:一般来讲当函数执行完成后,当前执行上下文(局部环境)会被弹出执行上下文栈并且销毁,控制权被重新交给执行栈上一层的执行上下文。

8.ES3执行上下文总结:

  • 8-1.函数被调用。
  • 8-2.在执行具体的函数代码之前,创建了执行上下文。
  • 8-3.进入执行上下文的创建阶段:
    • ①初始化作用域链。
    • ②创建 arguments object 检查上下文中的参数,初始化名称和值并创建引用副本。
    • ③扫描上下文找到所有函数声明:
      • α:对于每个找到的函数,用它们的原生函数名,在变量对象中创建一个属性,该属性里存放的是一个指向实际内存地址的指针。
      • β:如果函数名称已经存在了,属性的引用指针将会被覆盖。
    • ④扫描上下文找到所有 var 的变量声明:
      • α:对于每个找到的变量声明,用它们的原生变量名,在变量对象中创建一个属性,并且使用 undefined 来初始化。
      • β:如果变量名作为属性在变量对象中已存在,则不做任何处理并接着扫描。
    • ⑤确定 this 值。
  • 8-4.进入执行上下文的执行阶段:在上下文中运行/解释函数代码,并在代码逐行执行时分配变量值。

9.ES5中的执行上下文:

  • 9-1.ES5 规范又对 ES3 中执行上下文的部分概念做了调整,去除了 ES3 中变量对象和活动对象,以词法环境组件和变量环境组件替代。ES5的执行上下文概念上表示:
ExecutionContext = {
  ThisBinding = <this value>,
  LexicalEnvironment = { ... },
  VariableEnvironment = { ... },
}

10.ES6 词法环境定义:

  • 10-1.一个词法环境由环境记录器和一个可能的引用外部词法环境的空值组成。简单来说词法环境是一种持有标识符—变量映射的结构。这里的标识符指的是变量/函数的名字,而变量是对实际对象(包含函数类型对象)或原始数据的引用。

11.ES5中的变量环境:也是一个词法环境 ,所以它有着词法环境的所有特性。

  • 11-1.词法环境组件和变量环境的一个不同就是前者被用来存储函数声明和变量(let 和 const)绑定,而后者只用来存储 var 变量绑定。

12.ES5 执行上下文总结:

  • 12-1.程序启动,全局上下文被创建
    • ①创建全局上下文的词法环境
      • α:创建对象环境记录器 ,它用来定义出现在全局上下文中的变量和函数的关系(负责处理 let 和 const 定义的变量)
      • β:创建外部环境引用,值为 null
    • ②创建全局上下文的变量环境
      • α:创建对象环境记录器,它持有变量声明语句在执行上下文中创建的绑定关系(负责处理 var 定义的变量,初始值为 undefined 造成声明提升)
      • β:创建 外部环境引用,值为 null
    • ③确定 this 值为全局对象(以浏览器为例,就是 window )
  • 12-2.函数被调用,函数上下文被创建
    • ①创建函数上下文的词法环境
      • α:创建声明式环境记录器,存储变量、函数和参数,它包含了一个传递给函数的 arguments 对象(此对象存储索引和参数的映射)和传递给函数的参数的 length。(负责处理 let 和 const 定义的变量)。
      • β:创建外部环境引用,值为全局对象,或者为父级词法环境(作用域)。
    • ②创建函数上下文的变量环境
      • α:创建声明式环境记录器 ,存储变量、函数和参数,它包含了一个传递给函数的 arguments 对象(此对象存储索引和参数的映射)和传递给函数的参数的 length。(负责处理 var 定义的变量,初始值为 undefined造成声明提升)
      • β:创建外部环境引用,值为全局对象,或者为父级词法环境(作用域)
    • ③确定 this 值。
  • 12.3.进入函数执行上下文的执行阶段:在上下文中运行/解释函数代码,并在代码逐行执行时分配变量值。

13.执行上下文栈:

  • 13-1.管理执行上下文。执行上下文栈是栈结构的,因此遵循 LIFO(后进先出)的特性,代码执行期间创建的所有执行上下文,都会交给执行上下文栈进行管理。当 JS 引擎开始解析脚本代码时,会首先创建一个全局执行上下文,压入栈底。每当引擎发现一处函数调用,就会创建一个新的函数执行上下文压入栈内,并将控制权交给该上下文,待函数执行完成后,即将该执行上下文从栈内弹出销毁,将控制权重新给到栈内上一个执行上下文。
    代码示例:
function first() {
  console.log('first');
  second();
  console.log('first2');
}
function second() {
  console.log('second');
}
first();
console.log('window');
// first
// second
// first2
// window

上述代码的执行上下文栈:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值