一、作用域链
JavaScript是基于词法作用域的语言,当代码在一个环境中执行时,就会创建与之关联的作用域链(scope chain),这个作用域链可以看做是一个对象或者链表,对象中定义了这段代码“作用域”中的所有变量。
当需要查找某一变量的值时(这个过程称作“变量解析”),JavaScript会从链中的第一个对象开始查找,如果找到了则直接使用这个属性的值,查找不到则继续查找链上的下一个对象,以此类推,如果整个作用域链中都没有任一对象包含该属性,则会抛出一个引用错误(ReferenceError
)异常。
- 如果执行环境是函数,则其活动对象最开始只包含一个变量,即
arguments
对象(这个对象在全局环境是不存在的) - 全局执行环境的变量对象始终都是作用域链中的最后一个对象。
可以通过一段代码来理解作用域链:
var color = "blue";
function changeColor() {
var anotherColor = "red";
function swapColors() {
var tempColor = antherColor;
antherColor = color;
color = tempColor;
// 这里可以访问color、anotherColor和tempColor
}
// 这里可以访问color和anotherColor,但不能访问tempColor
swapColors();
}
// 这里只能访问color
changeColor();
作用域链图:
图中矩形表示了三个特定的执行环境:全局环境、changeColor()
的局部环境和swapColors()
的局部环境。其中,内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量或函数。
二、闭包
理解了作用域链,我们再来理解闭包是什么。
我们都知道在JavaScript中,只有函数内部的子函数能够访问局部变量,那么如果函数外部要访问函数内的局部变量应该怎么做呢?闭包就是用来解决这一问题的。
闭包是指有权访问另一个函数作用域中的变量的函数。
可以看一下阮一峰老师的例子:
function f1() {
var n = 999;
nAdd = function() { n += 1 }
function f2() {
alert(n);
}
return f2;
}
var result = f1();
result(); // 999
nAdd();
result(); // 1000
在这个例子中,result
就是闭包f2
函数,第一次运行的值是999,说明访问到了函数f1
内部的变量。第二次运行的值是1000,这是由于f2
被赋给了全局变量result
,由于result
引用着f2
,因此f2
不会因为运行结束了就被销毁,它将一直存在于内存当中,f1
和它的局部变量n
也是如此。
f1
的nAdd
由于没有使用var进行声明,因此aAdd
是一个全局变量,f1
执行后将function() { n + 1 }
赋给了全局变量nAdd
,此时function() { n + 1 }
也是一个闭包,同样可以对f1
内部的变量进行操作。
总结:
- 闭包可以读取函数内部的变量;
- 闭包会使得函数内部的变量都被保存在内存中,造成较大的内存开销,因此不要滥用闭包。解决的方法是在退出函数之前将不使用的局部变量置为
null
;
关于this对象
了解完闭包,还有一个需要特别注意的点。就是在闭包中使用this
的问题,看下面的代码:
var name = "The Window";
var object = {
name: "My Object",
getName: function() {
return function() {
return this.name;
};
}
};
alert(object.getNameFunc()()); // "The Window"(在非严格模式下)
这里要注意两个点:
this
对象是在运行时基于函数的执行环境绑定的- 匿名函数的执行环境具有全局性
因此这里的this
指向的是全局环境,所以查找到的是全局的name
。
那么闭包如何访问到object
对象呢,我们可以改变一下代码:
var name = "The Window";
var object = {
name: "My Object",
getName: function() {
var that = this;
return function() {
return that.name;
};
}
};
alert(object.getNameFunc()()); // "My Object"
可以看到,将this对象保存在一个闭包能够访问到的变量哩,就可以让闭包访问到该对象了。
结尾
系列文章: