JavaScript 执行上下文

一、执行上下文

1-1 什么是执行上下文

执行上下文就是JavaScript代码被解析和执行时所在环境的抽象概念,JavaScript中运行任何的代码都是在执行上下文中运行

1-2 执行上下文类型

  1. 全局执行上下文:不在函数中的代码都位于全局执行上下文中,一个程序中只能存在一个全局执行上下文,全局执行上下文会创建一个全局对象(在浏览器中就是window对象),然后将this指针指向这个全局对象

  2. 函数执行上下文:每次函数被调用时,都会为该函数创建一个新的执行上下文,一个程序中可以存在任意数量的函数执行上下文

  3. Eval函数执行上下文

1-3 变量对象

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

  • 在全局上下文中,变量对象就是全局对象

  • 在函数上下文中,我们用活动对象(activation object, AO)来表示变量对象。活动对象和变量对象其实是一个东西,只是变量对象是规范上的或者说是引擎实现上的,不可在 JavaScript 环境中访问,只有到当进入一个执行上下文中,这个执行上下文的变量对象才会被激活,所以才叫 activation object 呐,而只有被激活的变量对象,也就是活动对象上的各种属性才能被访问。活动对象是在进入函数上下文时刻被创建的,它通过函数的 arguments 属性初始化。arguments 属性值是 Arguments 对象

1-4 执行上下文生命周期

执行上下文的生命周期包括三个阶段:创建阶段 -> 执行阶段 -> 回收阶段

创建阶段:

  • 函数执行上下文

① 初始化函数的参数 arguments ,提升函数声明和变量声明

② 创建作用域链(作用域链是在创建变量对象之后创建的。因为作用域链是用于解析变量,当被要求解析变量时,JavaScript始终从代码嵌套的最内层开始,如果最内层没有找到变量,就会跳转到上一级的父作用域中查找,直到找到该变量)

③ 确定 this 指向

  • 全局执行上下文

在一段JS脚本执行之前,会先解析代码(所以说 JS 是解释执行的脚本语言),解析的时候会先创建一个全局执行上下文环境,然后提升函数声明和变量声明。变量先赋值为undefined,函数则先声明好,这一步做完了,然后开始正式执行程序。

执行阶段: 执行变量赋值、代码执行

回收阶段: 执行上下文出栈等虚拟机回收执行上下文

二、变量提升

参考《变量提升

三、确定this指向

概念:this 的值是在函数执行的时候才能确认,定义的时候不能确认。因为this是执行上下文环境的一部分,而执行上下文需要在代码执行之前确定,而不是定义的时候

3-1 四种基本情况

情况一:直接调用
function foo() {
  console.log(this) 
}
foo() // 非严格模式 window  严格模式 undefined
// 对于直接调用 foo 来说,不管 foo 函数被放在了什么地方,this 一定是 window 或 undefined

情况二:定义在对象中的函数被调用
let obj = {
    name:'hzy'
    foo:function(){
        console.log(this.name)    
    }、
}
obj.foo() //hzy

// 函数的定义位置不影响其this指向,this指向只和调用函数的对象有关。
let obj = { name:'hzy' }
function foo(){
    console.log(this.name)    
}
obj.foo = foo
obj.foo()	//hzy

情况三:构造函数中的this
function Foo(){
  this.name = 'hzy';
}
let f = new Foo()
console.log(f.name) // 构造函数中的this与被实例化的新对象绑定。

情况四:箭头函数
let p = {
  a: function () {
    var obj = {
      b: () => {console.log(this)},
    }
    obj.b()
  }
}
p.a() // {a: ƒ} 函数执行上下文的this值
// 箭头函数不绑定this, 它会捕获其定义时的位置上下文的this值, 作为自己的this值
复制代码

3-2 this指向案例

// 案例1:setTimeout & setInterval 延时器中的普通回调函数的this,指向window
function Person(){
    this.age = 0
    setTimeout(function(){
        console.log(this)    
    },3000)
}
const p = new Person() //window

// 案例2:
function Person(){
    this.age = 0
    setTimeout(()=>{console.log(this)},3000)
  	// 箭头函数捕获其定义时的位置上下文的this值,这里的上下文是构造函数Person
  	// 构造函数中的this与被实例化的新对象绑定
}
const p = new Person()  //Person{age:0}
复制代码

四、执行上下文栈

每次调用函数创建一个新的执行上下文,JavaScript引擎创建了执行上下文栈来管理执行上下文,可以把执行上下文栈认为是一个存储函数调用的栈结构,遵循先进后出的原则。

例子一:

上述流程图几个重要关键点

① JavaScript 执行在单线程上,所有的代码都是排队执行

② 一开始浏览器执行全局的代码时,首先创建全局的执行上下文,压入执行栈的顶部

每当执行一个函数就会创建函数的执行上下文,并且把它压入执行栈的顶部;当前函数执行完成后,当前函数的执行上下文出栈,并等待垃圾回收

④ 浏览器的 JS 执行引擎总是访问栈顶的执行上下文

全局上下文只有唯一的一个,它在浏览器关闭时出栈

例子二:

var color = "blue";
function changeColor() {
    var anotherColor = "red";
    function swapColors() {
        var tempColor = anotherColor;
        anotherColor = color;
        color = tempColor;
    }
    swapColors();
}
changeColor();
复制代码

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享