1. 执行上下文(Execution Context)
在JavaScript中,最基本的执行单位是执行上下文(Execution Context)。执行上下文可以理解为一段代码执行时所在的环境,用于存储变量、函数等内存信息,以及确定代码执行的顺序。
在JavaScript中,每当函数被调用时,都会创建一个执行上下文,并且使用执行栈(Execution Stack)来管理这些执行上下文。
一个执行上下文对象包含3个重要属性:变量对象(Variable Object,VO)、作用域链(Scope Chain)和this值,下面分别介绍。
1.1 变量对象(Variable Object)
变量对象是执行上下文中的一个重要属性,它用于存储变量、函数等内存信息。当JavaScript代码执行到一个函数时,会创建一个新的执行上下文,并为该函数创建一个变量对象。
变量对象包括:
函数参数
函数声明
变量声明
其中,函数声明和变量声明都会被提升到当前作用域的顶部,因此在函数内部可以使用变量和函数,即使在声明之前也可以正常使用。
console.log(foo); // undefined
var foo = 'bar';
console.log(foo); // 'bar'
console.log(func); // function func(){...}
func();
function func(){...}
在上面的例子中,变量和函数的声明都被提升到了函数作用域的顶部,因此在声明之前就可以正常使用。
1.2 作用域链(Scope Chain)
作用域链是指一个函数执行时,变量查找的路径,它由函数的活动对象(Activation Object)以及所有父级函数的活动对象组成。
当JavaScript查找一个变量时,先查找当前函数的活动对象,如果找不到就沿着作用域链往上查找,直到全局作用域。如果全局作用域都没找到,就会返回undefined。
var x = 1;
function test() {
var y = 2;
function innerTest() {
var z = 3;
console.log(x, y, z);
}
innerTest();
}
test(); // 1 2 3
在上面的例子中,innerTest函数中的变量z只能在该函数内部访问,变量y只能在innerTest函数的父级函数test中访问,变量x则可以在全局作用域中访问。
1.3 this值
this值是指在函数调用时,所在的执行上下文的一个属性,它代表函数执行的上下文对象。
this值的具体取值,取决于函数的调用方式,通常有以下几种情况:
作为函数调用(函数名()):this指向全局对象(浏览器中为window对象)。
作为方法调用(obj.func()):this指向调用该方法的对象(即方法所在的对象)。
使用apply/call/bind调用:this可以被显式指定。
作为构造函数调用(new构造函数()):this指向新创建的对象。
2. 执行栈(Execution Stack)
执行栈是用于管理JavaScript执行上下文的一种数据结构,它是一个后进先出(LIFO)的栈。
当一个函数被调用时,JavaScript会创建一个新的执行上下文,并将它压入执行栈的栈顶。当函数执行完毕后,它的执行上下文会被弹出执行栈。
function a() {
console.log('a');
b();
}
function b() {
console.log('b');
}
function c() {
console.log('c');
}
a();
c();
在上面的代码中,依次执行了a、b、c三个函数,执行栈的变化如下图所示:
![执行栈示意图](https://img-blog.csdn.net/20171018172058516?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXdlcnR5dHJhbnNwb3J0/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/q/85)
在执行上述代码时,首先执行函数a,而函数a中又调用了函数b,因此函数b被压入执行栈的栈顶。当函数b执行完毕后,它的执行上下文被弹出执行栈,执行上下文回到了函数a中,接着执行函数a中的其他语句。当函数a执行完毕后,它的执行上下文也被弹出执行栈,最后执行函数c中的内容。
3. 总结
执行上下文和执行栈是JavaScript中非常重要的概念,它们决定了JavaScript代码的执行顺序,以及变量、函数的生命周期。
执行上下文包含变量对象、作用域链和this值三个属性,用于存储变量、函数等内存信息,以及确定代码执行的顺序;执行栈则用于管理JavaScript执行上下文的顺序,它是一个后进先出(LIFO)的栈。
深入理解执行上下文和执行栈对于理解JavaScript的运行机制和调试代码非常有帮助,是每个前端工程师必须掌握的知识之一。