闭包是指有权访问另一个函数(父函数)作用域中的变量的函数
无论什么时候在函数中访问一个变量时,就会从作用域链中搜索具有相应名字的变量。一般来讲,当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域。所以在函数外部自然无法读取函数内的局部变量, 但是闭包的情况不同,它可以在外部访问函数内部变量。如例所示:
function myFunction() {
var name = 'Bob';
function displayName() {
console.log(name);
}
return displayName;
}
var myFunc = myFunction();
myFunc();
myFunction()返回了一个闭包:这个闭包由displayName()函数和闭包创建时存在的name变量组成.由于 displayName()持有了name的引用,myFunc持有了displayName()的引用,因此myFunc调用时,name还是可以被访问
由于闭包会携带包含函数的作用域(闭包会将包含它的函数即父函数的活动对象添加到它的作用域链中,这就是为什么闭包可以访问包含函数中的变量的原因),因此会比其它函数占用更多的内存(因为包含闭包的父|外部函数的变量被保存在内存中)。
使用闭包的注意点
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包可以在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
闭包与变量
闭包只能取得包含函数中任何变量的最后(最新的值)一个值(这是因为可以在父函数外部通过闭包修改父函数内部变量的值),这是因为闭包所保存的是整个变量对象,而不是某个特殊的变量。
function createFunctions(){
var result = new Array();
for(var i=0; i<10; i++){
result[i] = function(){
return i;
};
}
return result;
}
这个函数返回一个函数数组,每个函数都返回10。这是因为每个函数的作用域链中都保存着createFunctions()的活动对象,所以它们都引用的都是同一个变量i。当循环执行完,变量i的值是10,此时每个函数都引用着保存变量i的同一个变量对象,所以每个函数内部i的值都是10。
对于上面的情况,如果我们改变代码如下:
function createFunctions(){
var result = new Array();
for(var i=0; i<10; i++){
result[i] = function(num){
return function(){
return i;
};
}(i);
}
return result;
}
此时函数数组内每个函数内部i的值就是从0到9了
闭包中的this对象
var name = "The Window";
var obj = {
name: "My Object",
getName: function(){
//外部函数的变量等
return function(){ //this和arguments
return this.name;
};
}
};
console.log(obj.getName()()); // The Window
每个函数在被调用时都会自动获取两个特殊变量:this和arguments,内部函数在搜索这两个变量时,只会搜索到其活动对象为止。obj.getName()()实际上是在全局作用域中调用了匿名函数,this指向了window。
var name = "The Window";
var obj = {
name: "My Object",
getName: function(){
var that = this;
return function(){ //闭包
return that.name;
};
}
};
console.log(obj.getName()()); // My Object
让外部作用域中(包含函数的作用域)的this对象保存在一个闭包能够访问到的变量里,就可以让闭包访问该对象了(如上就是name变量)
闭包用途其一 模仿块级作用域
(function() {
// 块级作用域(通常称为私有作用域)
}) (); //定义并立即调用了一个匿名函数。既可以执行其中的代码,又不会在内存中留下对该函数的引用
function outputNumbers(count){
(function(){
for(var i = 0; i < count; i++){
console.log(i);
}
})();
console.log(i); //导致一个错误!i没有定义
}
变量i只能在循环中使用,使用后即被销毁。而在私有作用域中能够访问变量count,是因为这个匿名函数是一个闭包,它能够访问包含作用域中的所有变量。
- 好处:过多的全局变量和函数很容易导致命名冲突,而通过创建私有作用域,每个人既可以使用自己的变量,又不用担心搞乱全局作用域。
闭包用途其二 创建私有变量
function MyObject(){
// 共有变量 可在外部直接访问
this.number = 50;
// 私有变量
var privateVariable = 10;
// 私有函数
function privateFunction(){
return false;
}
// 特权方法,调用私有方法、函数
this.publicMethod = function(){
privateVariable++;
return privateFunction();
}
}
我们把有权访问私有变量、私有函数的共有方法称为特权方法。特权方法是作为闭包有权访问在构造函数中定义的所有变量和函数。