定义
一个变量的作用域(scope)是程序源代码中定义这个变量的区域。
全局变量
全局变量拥有全局作用域,在JavaScript代码中的任何地方都是有定义的。
局部变量
在函数体内部声明的变量只在函数体内有定义,它们是局部变量,作用域是局部性的,函数的参数也是局部变量,它们只在函数体内有定义。
优先级
在函数体内,局部变量的优先级高于同名的全局变量。如果在函数内声明的一个局部变量或者函数参数中带有的变量和全局变量重名,那么全局变量就被局部变量所覆盖。
var scope = "global";
function checkScope() {
var scope = "local";
return scope;
}
checkScope(); // => "local"
作用域嵌套
var scope = "global scope";
function checkScope(){
var scope = "local scope";
function nested() {
var scope = "nested scope";
return scope;
}
return nested();
}
checkScope(); // => "nested scope"
函数作用域
变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。
function foo() {
for (var i = 0; i < 5; i++) {
console.log(i); // 0~4
}
console.log(i); // 5
}
声明提前
JavaScript函数里声明的所有变量(但不涉及赋值)都被提交至函数体的顶部。
var scope = "global";
function foo() {
console.log(scope); // undefined
var scope = "local";
console.log(scope); // "local"
}
在上述代码中,变量scope在函数体内开头打印,却不是 global,而是undefined,这是因为函数体内的局部变量 scope 声明提前,但是声明提前不涉及赋值,也就是说局部变量 scope 在声明提前的时候为 undefined,由于局部变量优先级高于全局变量,所以覆盖了全局变量,也就能理解为什么输出 undefined。
作用域链
如果我们将局部变量看作是自定义实现的对象的属性的话,那么我们可以换个角度来看变量的作用域。
每一段JavaScript代码(全局代码或者函数)都有一个与之关联的作用域链(scope chain),这个作用域链是一个对象列表或者链表,这组对象定义了这段代码“作用域中”的变量。
- 在最顶层的代码中(即不包含在任何函数定义内的代码),作用域链由一个全局对象组成。
- 在不包含嵌套函数的函数体内,作用域链上有两个对象,第一个是定义函数参数和局部变量的对象,第二个是全局对象。
- 在一个嵌套函数体内,作用域链上至少有三个对象,第一个是定义嵌套函数参数和其局部变量的对象,第二个则是定义嵌套函数时的作用域链(即父级函数的两个对象)。
function add(n1, n2){
var sum = n1 + n2;
return sum;
}
在函数add定义时,它的作用域链会加入一个全局对象,这个全局对象包含了所有的全局变量,如下图所示:
函数add执行时,会创建一个名为“声明上下文对象”的内部对象,该对象定义了函数执行时的环境。当声明上下文对象被创建时,它的作用域链的初始化为当前执行函数的[[scope]]所包含的对象。这些值又组成一个新的对象(活动对象),该对象被推入作用域链的最顶端,随着声明上下文被摧毁而一起摧毁,新的作用域链如图所示: