JavaScript是基于词法做用域的语言,当代码在一个环境中执行时,就会建立与之关联的做用域链(scope chain),这个做用域链能够看作是一个对象或者链表,对象中定义了这段代码“做用域”中的全部变量。
当须要查找某一变量的值时(这个过程称做“变量解析”),JavaScript会从链中的第一个对象开始查找,若是找到了则直接使用这个属性的值,查找不到则继续查找链上的下一个对象,以此类推,若是整个做用域链中都没有任一对象包含该属性,则会抛出一个引用错误(ReferenceError
)异常。
bash
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();
复制代码
做用域链图:app
changeColor()
的局部环境和
swapColors()
的局部环境。其中,内部环境能够经过做用域链访问全部的外部环境,但外部环境不能访问内部环境中的任何变量或函数。
理解了做用域链,咱们再来理解闭包是什么。
咱们都知道在JavaScript中,只有函数内部的子函数可以访问局部变量,那么若是函数外部要访问函数内的局部变量应该怎么作呢?闭包就是用来解决这一问题的。函数
闭包是指有权访问另外一个函数做用域中的变量的函数。post
能够看一下阮一峰老师的例子:ui
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
也是如此。this
f1
的nAdd
因为没有使用var进行声明,所以aAdd
是一个全局变量,f1
执行后将function() { n + 1 }
赋给了全局变量nAdd
,此时function() { n + 1 }
也是一个闭包,一样能够对f1
内部的变量进行操做。spa
总结:code
null
;了解完闭包,还有一个须要特别注意的点。就是在闭包中使用this
的问题,看下面的代码:cdn
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对象保存在一个闭包可以访问到的变量哩,就可让闭包访问到该对象了。
结尾
系列文章: