目录
- 执行上下文
- js代码执行环境
- 执行上下文生命周期
执行上下文
执行上下文Execution Context就相当于js代码执行的环境
js代码执行环境
- 全局环境
- 函数环境
- eval函数环境(不常用)
对应的执行上下文有
- 全局上下文
- 函数上下文
- eval函数上下文
在js代码开始运行时,会先生成全局上下文,然后遇到当前调用的函数生成此函数的上下文,并依次放入栈中
当进入一个执行环境时,它的执行上下文就会被创建,并被推入执行栈中(入栈);程序执行完成时,它的执行上下文就会被销毁,并从栈顶被推出(出栈),控制权交由下一个执行上下文。
因为 JavaScript 在执行代码时最先进入全局环境,所以处于栈底的永远是全局环境的执行上下文。而处于栈顶的是当前正在执行函数的执行上下文。
当函数调用完成后,它就会从栈顶被推出,理想的情况下,闭包会阻止该操作。
当我们使用递归函数不限制停止条件时,就会导致栈溢出
// 递归调用自身
function foo() {foo();
}
foo();
// 报错: Uncaught RangeError: Maximum call stack size exceeded
执行上下文生命周期
执行上下文的生命周期有两个阶段:
- 创建阶段(进入执行上下文):函数被调用时,进入函数环境,为其创建一个执行上下文,此时进入创建阶段。
- 执行阶段(代码执行):执行函数中代码时,此时执行上下文进入执行阶段。
创建阶段
创建阶段要做的事情主要如下:
-
创建变量对象(VO:variable object)
-
确定函数的形参(并赋值)
-
函数环境会初始化创建 Arguments对象(并赋值)
-
确定普通字面量形式的函数声明(并赋值)
-
变量声明,函数表达式声明(未赋值)
-
-
确定 this 指向(this 由调用者确定)
-
确定作用域(词法环境决定,哪里声明定义,就在哪里确定)
执行阶段
- 变量对象赋值
- 变量赋值
- 函数表达式赋值
- 调用函数
- 顺序执行其它代码
例如(伪代码如下)
function fn(a) {// 函数内容var m = 1;var a = a;var f = () => {}// 函数被调用,生成函数上下文// 创建变量对象{// 确定形参,赋值a: 3,// 舒适化传教 Arguments 对象,赋值arguments : { 0 : 3, length : 1; },// 确定普通字面量的函数声明,赋值f: undefined,// 变量声明m: undefined}// 确定this指向// 这个函数this指向全局// 确定作用域// 在本函数作用域// 进行执行阶段// 创建变量对象{// 确定形参,赋值a: 3,// 舒适化传教 Arguments 对象,赋值arguments : { 0 : 3, length : 1; },// 确定函数引用f: () => {},// 变量赋值m: 1}// 确定this指向// 这个函数this指向全局// 确定作用域// 在本函数作用域
}fn(3);
我们看到,只有在代码执行阶段,局部变量才会被赋予具体的值。在建立阶段局部变量的值都是 undefined。
这其实也就解释了变量提升的原理。
以下代码会输出什么
(function () {console.log(typeof foo);console.log(typeof bar);var foo = "Hello";var bar = function () {return "World";}function foo() {return "good";}console.log(foo, typeof foo);
})()
使用同样的方法来分析
(function () {console.log(typeof foo);console.log(typeof bar);var foo = "Hello";var bar = function () {return "World";}function foo() {return "good";}console.log(foo, typeof foo);// 函数开始调用,创建函数上下文// 创建变量对象{// 没有形参// arguments对象// 函数变量bar: undefined,// 变量,开始为undefined,后来创建了function foo() {// return "good";//}// 这样创建的函数foo本身就指向自己foo: (){return "good";}}// this指向// 确定作用域// 进入执行阶段console.log(typeof foo); // 'function'console.log(typeof bar); // 'undefined'// 变量对象{// 没有形参// arguments对象// 确定函数指向bar: function () {return "World";},foo: () {return "good";},// 变量赋值foo: "Hello" }// this指向// 确定作用域console.log(foo, typeof foo);// 输出 'Hello' 'string'
})()