理解闭包,首先要理解JavaScript的作用域。
>
JavaScript中是没有块级作用域的。
>
变量的作用域有两种:全局变量和局部变量。
>
JavaScript语言的特殊之处,就在于函数内部可以直接读取全局变量
上代码:
var n = 99;
function f1(){
alert(n);
}
f1();//99
以上函数,f1可以调用全局变量n;
但,函数外部是无法读取函数内部的局部变量。
function f1(){
var n = 99;//去掉var,n就是一个全局变量,alert(n)就不会报错。
}
alert(n);//ReferenceError: n is not defined
那么如何去访问函数内部的变量呢?
要得到函数内部的局部变量,就需要使用闭包。通过一个函数来访问函数内部的局部变量。
闭包的概念:
- 闭包就是函数的局部变量的集合,只是这些局部变量在函数返回后会继续存在。
- 闭包就是函数的“堆栈”在函数返回后并不释放,我们也可以理解为这些函数堆栈并不在栈上分配而是在堆上分配。
当在一个函数内定义另外一个函数就会产生闭包。
function f1(){ var n = 99; function f2(){ alert(n); } return f2;//f2函数就是闭包,通过这个函数能访问函数内部的局部变量。 } var result = f1();//result就是闭包f2函数 result();//弹出99
闭包的用途是什么呢?
- 可以读取函数内部的变量。
让这些变量始终保持在内存中。
function f1(){ var n = 99; nAdd = function(){//nAdd是一个全局变量,而不是局部变量。nAdd的值是一个匿名函数,这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以再函数外部对函数内部的局部变量进行操作。 n += 1; } function f2(){ alert(n); } return f2; } var result = f1(); result();//99 nAdd(); result();//100
这就证明了,函数f1中的变量n一直保存在内存中,并没有在f1被调用后被自动清除。
为什么会这样呢?原因在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致F2始终在内存中,而f2的存在依赖于f1,由此f1也始终在内存中,不会在调用结束后,被垃圾回收机制回收。
闭包的注意点
- 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄漏。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
- 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当做对象object使用,把闭包当做它的公用方法(public method),把内部变量当做它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
经典的闭包小案例
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()());//The Window
-
var name = "The Window";
var object = {
name: "My Object";
getNameFunc: functon(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()());//My Object
-
function fun(n, o){
console.log(o);
return {
fun: function(m){
return fun(m, n);
}
};
}
var a = fun(0);//undefined
a.fun(1);//0
a.fun(2);//0
a.fun(3);//0
var b = fun(0).fun(1).fun(2).fun(3);//undefined,0,1,2
var c = fun(0).fun(1);//undefined,0
c.fun(2);//1
c.fun(3);//1