引入
首先看两段代码
var foo=function(){
console.log("foo1");
}
foo();
var foo=function(){
console.log("foo2");
}
foo();
//foo1 foo2
function foo(){
console.log("foo1");
}
foo();
function foo(){
console.log("foo2");
}
foo();
//foo2 foo2
为什么会输出两个foo2?
js引擎执行代码,是一段一段执行的然后每执行一段代码前要进行一些准备工作(例如,第一个例子中的变量提升,第二个例子中的函数提升)
准备工作就是创建执行上下文
模拟第一段代码
ECStack.push(<checkscope> functionContext);
ECStack.push(<f> functionContext);
ECStack.pop();
ECStack.pop();
模拟第二段代码
ECStack.push(<checkscope> functionContext);
ECStack.pop();
ECStack.push(<f> functionContext);
ECStack.pop();
是什么
是执行js代码的环境
分为:
- 全局执行上下文 — 这是默认或者说基础的上下文,任何不在函数内部的代码都在全局上下文中。它会执行两件事:创建一个全局的 window 对象(浏览器的情况下),并且设置
this
的值等于这个全局对象。一个程序中只会有一个全局执行上下文。 - 函数执行上下文 — 每当一个函数被调用时, 都会为该函数创建一个新的上下文。每个函数都有它自己的执行上下文,不过是在函数被调用时创建的。函数上下文可以有任意多个。每当一个新的执行上下文被创建,它会按定义的顺序(将在后文讨论)执行一系列步骤。
- Eval 函数执行上下文 — 执行在
eval
函数内部的代码也会有它属于自己的执行上下文,但由于 JavaScript 开发者并不经常使用eval
,所以在这里我不会讨论它。
为什么
怎么办
案例
let a = 'Hello World!';
function first() {
console.log('Inside first function');
second();
console.log('Again inside first function');
}
function second() {
console.log('Inside second function');
}
first();
console.log('Inside Global Execution Context');
/**
* Inside first function
* Inside second function
* Again inside first function
* Inside Global Execution Context
*/
当上述代码在浏览器加载时,JavaScript 引擎创建了一个全局执行上下文并把它压入当前执行栈。
当遇到 first() 函数调用时,JavaScript 引擎为该函数创建一个新的执行上下文并把它压入当前执行栈的顶部。
当从 first()
函数内部调用 second() 函数时,JavaScript 引擎为 second()
函数创建了一个新的执行上下文并把它压入当前执行栈的顶部。
当 second()
函数执行完毕,它的执行上下文会从当前栈弹出,并且控制流程到达下一个执行上下文,即 first()
函数的执行上下文。
当 first()
执行完毕,它的执行上下文从栈弹出,控制流程到达全局执行上下文。
一旦所有代码执行完毕,JavaScript 引擎从当前栈中移除全局执行上下文。
浏览器加载的时候创造全局执行上下文,压入执行栈
然后每遇到一个函数执行,创建函数执行上下文,压入执行栈
创建执行上下文
1. 创建阶段 ---- 执行前的准备工作
- this 的绑定
- 创建作用域链。
- 变量对象。
2. 执行阶段 — 代码执行