我所认识的闭包
什么是闭包
我们可以把js中的每一个函数理解成一个闭包。但通过函数之间的嵌套更能体现出闭包的特性。
var counter = function () {
var count = 0;
var add = function () {
count ++ ;
return count;
}
return add;
}
var Timer = counter();
setInterval(function(){
console.log(Timer())// 1 2 3 4 5……
},1000)
在浏览器中运行以上代码后,我们会看到以下现象:每隔一秒我们就会在控制台看到一个新增加的数字。
不妨让我们来解读上边的代码:变量count是在函数counter内部定义的,只有在函数counter调用时,变量count才有生命周期。当函数conter从调用栈中返回时,其内部变量count原先申请的内存空间也随之被释放。可问题是,在上述代码中,当counter调用结束后,执行Timer()时,变量count非但没销毁,而且随着Timer的每一次调用,都会增加,这是为什么呢?
这正是闭包的特性。在函数外部可以改变和引用函数内部的变量。当一个函数返回一个内部定义的函数时,就形成了闭包。在返回这个内部函数的时候,也会返回这个函数的定义环境。怎么来理解定义环境呢?
var counter = function () {
var count = 0;
var add = function () {
count ++;
return count;
}
return add;
}
var counter1 = counter();
var counter2 = counter();
counter1();//1
counter2();//1
counter1();//2
counter1();//3
counter2();//2
就像你看到的,couner1和counter2是分别调用counter()生成的2个不同的闭包实例,他们内部调用的变量count分别属于各自的运行环境!我们可以这么来理解:在调用counter()返回add函数时,也会把add函数调用的counter函数内部的所有用到的变量(比如:count)一块返回,并保存作为一个副本,每个闭包实例之间相互独立,互不影响。
相信接下来的这个例子,好多人是再熟悉不过了,不妨看看。
for(var i=0; i<10; i++){
var obj = document.createElement("div");
obj.innerHTML=i;
obj.onclick = function(){
alert(i)
}
document.body.appendChild(obj);
}
执行完之后,页面上会出现10个div,我们原本想让点击每一个div的时候,弹出显示对应div的索引,可是事与愿违,总是弹出10,为什么呢?这是因为在执行点击的时候,变量i已经累计到10了,所以总是弹出10,那么如何得到我们想要的效果呢?
for(var i=0; i<10; i++){
(function (n) {
var obj = document.createElement("div");
obj.innerHTML=n;
obj.onclick = function(){
alert(n)
}
document.body.appendChild(obj);
})(i)
}
闭包的用途
除了上述提到的闭包能在函数外部访问函数内部的变量,以及造成内存泄漏等特性外,外包主要在以下2个方面应用较多:
♣ 嵌套的回调函数
在函数式编程中,经常会遇到在一个业务执行完成之后吊起另一个业务的关系,就是我们所说的回调。那么闭包在回调的使用更加明显。
$.getJSON(url).done(function(res){
return function () {
//处理返回结果
}
}).fail(function () {
//处理失败提示
})
♣ 隐藏对象的细节
通过上述提到的代码,不难看出,如果想访问counter内部的变量count,必须调用函数Timer()。那么受这个启发,我们也可以把对象封装起来,只返回一个”返回器“对象,以此来实现对对象属性的隐藏。
var privateObj = function (){
return function (){
return {
name:"属性1",
val:"值1"
}
}
}
为什么要这么做呢?对象没有私有属性,也就是说对象的每一个属性都是曝露给外部的。这样可能会有安全隐患,譬如对象的使用者直接修改了某个属性,导致对象内部数据的一致性受到破坏等。
浙公网安备 33010602011771号