以下为个人理解。
js中,一个函数的返回值为一个子函数时,比如:
function A(a) {
var m=10;
return function B(b){
console.log(a+b+m);
}
//可能还有其它子函数和语句
}
这种情况下,当调用函数A后,A就整体驻留在内存中了,包括变量和子函数。然后我们可以调用返回的子函数B,而B可以使用母函数A中驻留的变量的值,包括传入参数和内部变量,也可以调用A中的其它子函数(若存在)。
var c1=A(1);
var c2=A(2);
c1.B(1); //输出12
c2.B(1); //输出13
函数A就称为“闭包(closure)”,它是子函数和数据的封装,返回的子函数可以和普通函数一样被外界使用。闭包和c++中的"对象(object)"类似,可以说是简化的"对象"。闭包适用于多个实体具有相同处理流程的情况。闭包在回调函数中很有用:当多个页面实体使用相同的回调函数时,回调函数需要判断是哪个实体调用了它,但是由于回调函数的传入参数是系统限定的,无法直接判断是哪个实体;解决办法是把回调函数封装到闭包内部,然后给每个实体创建一个闭包实例,每个闭包实例的母函数传入对应实体的特征数据,即实现了绑定。当回调函数被系统调用时,即可以处理对应的实体事件。见后。
说明:闭包函数内部如存在其它子函数,但没有return,则外界不能访问;闭包内部的变量也不能从外部访问。这些是闭包私有的。
下面看看闭包在书写上的格式:
1、闭包只返回一个匿名成员函数的情况:
function myFunc(初始参数) {
return function(动态参数){
...
}
}
实例化:
var myClosure1= myFunc(初始参数1);
var myClosure2= myFunc(初始参数2);
调用:
myClosure1(动态参数1);
myClosure2(动态参数2);
2、闭包返回有名称的子函数的情况,调用时写法和c++使用对象类似,举例:
var makeCounter = function(InitParm) {
var privateCounter = InitParm;
function changeBy(val) { //无return,相当于私有函数,不能外部调用
privateCounter += val;
}
return { //返回多个子函数
increment: function(D) {
changeBy(D);
},
value: function() {
return privateCounter;
}
}
};
var Counter1 = makeCounter(0);
Counter1.increment(1);
console.log(Counter1.value());
3、可以用“立即执行函数”来定义和初始化
var myClosure = function(初始参数) {
return function(动态参数){
...
}
}(初始参数值);
调用:
myClosure(动态参数);
下面看看闭包在回调函数的使用。
假如有一组页面元素,已保存为一个数组。为了点击不同的元素时显示不同的值,以下写法有问题:
for(var i=0;i<max;i++)
{
每个i值所对应的html元素.onmousedown=function(e){console.log(i);}
}
这样是不行的。当mousedown触发时,回调函数(子函数)去读取母体中i的值,但这时候i的值为max,所有的元素点击都显示max。
所以,引入一层闭包,让每个闭包实例保存不同的初始值。
for(var i=0;i<max;i++)
{
每个i值所对应的html元素.onmousedown=function(x){return function(e){console.log(x);}}(i);
}
每次循环都初始化一个闭包,保存了不同的i。
参考文章:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures