1. Javascript中的执行上下文类型
Javascript中有三种执行上下文类型:全局执行上下文、函数执行上下文和Eval函数执行上下文。
全局执行上下文是在代码执行之前创建的,而函数执行上下文是在函数调用时创建的。Eval函数执行上下文是在代码执行期间由eval()函数创建的。
下面分别介绍这三种执行上下文类型。
1.1 全局执行上下文
全局执行上下文是默认的执行上下文,它在代码执行之前创建,当页面加载完成后便会创建。
代码执行过程中,只会存在一个全局执行上下文,全局执行上下文中的变量、函数等都是全局可访问的。
下面是一个全局执行上下文的示例:
var a = 1;
function foo() {
console.log('hello');
}
在代码执行之前,就已经创建了全局执行上下文,此时全局执行上下文中包含:
{
VO: {
a: 1,
foo:
},
Scope: [global]
}
VO是variable object的简称,代表变量对象,表示执行上下文中的变量、函数等信息。
Scope代表作用域,全局执行上下文的作用域为全局作用域。
1.2 函数执行上下文
函数执行上下文是在函数调用时创建的,每次调用函数都会创建新的函数执行上下文。
函数执行上下文中包含函数的参数、变量、函数等信息。
下面是一个函数执行上下文的示例:
function foo(a, b) {
var c = a + b;
console.log(c);
}
foo(1, 2);
在调用foo函数时,会创建一个新的函数执行上下文,此时该函数执行上下文中包含:
{
VO: {
a: 1,
b: 2,
c: undefined,
arguments: {0: 1, 1: 2, length: 2},
foo:
},
Scope: [foo's scope, global]
}
arguments是一个类数组对象,包含了函数的参数信息。
1.3 Eval函数执行上下文
Eval函数执行上下文是在代码执行期间由eval()函数创建的执行上下文。
由于eval()函数会执行传入的字符串代码,所以Eval函数执行上下文中包含了该字符串中的变量、函数等信息。
下面是一个Eval函数执行上下文的示例:
var a = 1;
eval('var b = 2; console.log(a + b);');
在执行eval()函数时,会创建一个新的Eval函数执行上下文,此时该函数执行上下文中包含:
{
VO: {
b: 2
},
Scope: [global]
}
可以看到,Eval函数执行上下文中只包含了代码中定义的变量b,而全局作用域中的其他变量并没有包含在其中。
2. 执行上下文的创建
在Javascript中,执行上下文的创建分为两个阶段:
创建阶段
代码执行阶段
2.1 创建阶段
在创建阶段,Javascript会先创建VO,然后将VO和作用域链等信息添加到执行上下文中,最后创建this。
下面是一个创建阶段的示例:
function foo() {
var a = 1;
console.log(a);
}
foo();
在调用foo函数时,会经过执行上下文的创建阶段,此时执行上下文中包含:
{
VO: {
a: undefined,
foo:
},
Scope: [foo's scope, global]
}
可以看到,在创建阶段中,Javascript先创建了VO,然后将VO添加到执行上下文中,此时a的值为undefined。
2.2 代码执行阶段
在代码执行阶段,Javascript会按照代码的顺序执行代码,并更新执行上下文中的变量等信息。
下面是一个代码执行阶段的示例:
function foo() {
var a = 1;
console.log(a);
}
foo();
在调用foo函数时,会先经过执行上下文的创建阶段,然后进入代码执行阶段,在代码执行阶段中,Javascript会按照代码的顺序执行代码:
{
VO: {
a: undefined,
foo:
},
Scope: [foo's scope, global]
}
{
VO: {
a: 1,
foo:
},
Scope: [foo's scope, global]
}
可以看到,在代码执行阶段中,Javascript会先执行var a = 1;语句,然后输出a的值。
3. 执行上下文的销毁
当Javascript完成代码的执行之后,会销毁当前的执行上下文,此时执行上下文中的变量、函数等都会被销毁。
下面是一个执行上下文销毁的示例:
function foo() {
var a = 1;
console.log(a);
}
foo();
在调用foo函数时,会先创建一个新的执行上下文,然后在代码执行完毕后销毁该执行上下文,此时执行上下文中的变量a也被销毁了。
4. 执行上下文的作用域
执行上下文的作用域是指变量、函数等的可访问范围,在Javascript中,执行上下文的作用域是由作用域链来实现的。
作用域链是由多个执行上下文对象组成,每个执行上下文对象包含一个Scope属性,其中存储着它的外部执行上下文的引用。
下面是一个作用域链的示例:
var a = 1;
function foo() {
var b = 2;
console.log(a + b);
}
foo();
在调用foo函数时,会创建一个新的执行上下文,并将它的Scope属性指向全局执行上下文的变量对象。
这样在执行foo函数中的console.log(a + b)时,Javascript会先在foo函数的变量对象中查找变量b的值,如果找到则直接使用,否则就会沿着作用域链向上查找,直到找到全局执行上下文的变量对象并查找到变量a的值。
5. 执行上下文与this关键字
this关键字表示当前函数执行的上下文环境,在Javascript中,this的值是动态变化的,根据函数的调用方式不同,this的值也不同。
下面介绍几种常见的函数调用方式及this的取值:
5.1 普通函数调用
当函数作为普通函数调用时,this的值指向全局对象。
function foo() {
console.log(this);
}
foo(); // 输出window对象
5.2 方法调用
当函数作为对象的方法调用时,this的值指向该对象。
var obj = {
name: 'John',
getName: function() {
console.log(this.name);
}
};
obj.getName(); // 输出John
5.3 构造函数调用
当函数通过new关键字作为构造函数调用时,this的值指向新创建的对象。
function Person(name, age) {
this.name = name;
this.age = age;
}
var p = new Person('John', 20);
console.log(p); // 输出{name: "John", age: 20}
5.4 apply/call调用
当函数通过apply、call等方法调用时,this的值可以通过传入的第一个参数来指定,如果没有传入参数,则指向全局对象。
function getName() {
console.log(this.name);
}
var obj = {
name: 'John'
};
getName.apply(obj); // 输出John
6. 小结
执行上下文是Javascript中重要的概念,理解它对于深入理解Javascript的运行机制非常有帮助。
Javascript中有三种执行上下文类型:全局执行上下文、函数执行上下文和Eval函数执行上下文。执行上下文的创建分为两个阶段:创建阶段和代码执行阶段。执行上下文的作用域是由作用域链来实现的。this关键字表示当前函数执行的上下文环境,在不同的函数调用方式下,this的值也不同。