欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JS闭包的详解

程序员文章站 2024-03-17 23:50:04
...

一、什么是闭包?

特点:
1 函数嵌套函数
2 内部函数可以引用外部的参数和变量
3 参数和变量不会被垃圾回收机制所回收

举一个简单的闭包的例子,例子中符合闭包的三个特点

function aaa(a){
    var b = 5;
    function bbb(){
        alert(a);
        alert(b);
    }
}
aaa();  

关于js 中的垃圾回收机制,这里说明一下。请看下面代码,当aaa函数执行完毕之后,aaa中的变量a会被js的垃圾回收机制所回收

function aaa(){
    var a = 1;
}
aaa(); // 改函数执行完毕时候,函数中的变量a会被js的垃圾回收机制所回收  

再举一个闭包例子

当aaa函数执行完毕之后,调用c函数仍然可以弹出1,说明了a这个变量没有被垃圾回收

function aaa(){
    var a = 1;
    function bbb(){
        alert(a);
    }
    return bbb;
}
var c = aaa(); // 当aaa函数执行完毕之后,调用c函数仍然可以弹出1,说明了a这个变量没有被垃圾回收
c();  

二、闭包有什么好处?应用在哪?

2.1 好处:

1 可以使一个变量常驻内存
2 避免全局变量的污染
3 私有成员变量的存在

举个例子,例子中,使用全局变量a实现每次调用aaa函数,变量a++,过多使用全局变量不容易维护(全局变量的污染)。

var a = 1;
function aaa(){
    a++;
    alert(a);
}
aaa(); //2
aaa(); //3
alert(a); // 这里的a是全局变量  

下面,使用闭包实现这个例子:
由于a是局部变量,防止了全局变量污染

function aaa(){
    var a = 1;
    return function bbb(){
        a++;
        alert(a);
    }
}
var b = aaa();
b(); //2
b(); //3
alert(a); // 由于a是局部变量,外面无法引用,程序报错  

使用函数表达式的形式,改造上面例子
关于函数表达式的知识点,可以查看JS函数声明与函数表达式的区别 http://www.cnblogs.com/linhp/p/6085567.html

var aaa = (function (){
    var a = 1;
    return function bbb(){
        a++;
        alert(a);
    }
})();// 这里已经执行了
aaa(); //2
aaa(); //3  

私有成员变量的例子:
将表达式中的函数bbb与ccc通过json的方式返回,外部只能调用返回的json中的函数bbb和ccc,而无法访问a这个成员变量。

var aaa = (function (){
    var a = 1;
    function bbb(){
        a++;
        alert(a);
    }
    function ccc(){
        a++;
        alert(a);
    }
    return {b:bbb,c:ccc}
})();
aaa.b();
aaa.c();  

2.2 用法:

1 模块化代码
2 在循环中,直接找到对应元素的索引

模块化代码就不举例了,上面的代码都能体现
下面举例(在循环中,直接找到对应元素的索引)
先看下面例子:

<script>
window.onload = function (){
    var oLi = document.getElementsByTagName("li");
    for (var i = 0; i < oLi.length; i++) {
        oLi[i].onclick = function (){
            alert(i);
        }
    }
}
</script>
<ul>
    <li>111111111111</li>
    <li>222222222222</li>
    <li>333333333333</li>
</ul>  

上面例子中,想通过给每一个li添加点击事件,点击每个li,弹出li的索引,而上面例子中,由于for循环已经结束,而onclick事件还没触发,此时i已经等于3了,所以你无论点击哪一个li都无法获取索引,弹出的值都是3

下面,通过闭包的形式,改造,获取索引

window.onload = function (){
    var oLi = document.getElementsByTagName("li");
    for (var i = 0; i < oLi.length; i++) {
        (function(i){
            oLi[i].onclick = function (){
                alert(i);
            }
        })(i); //循环过程中,这里已经执行了。参数i永驻内存,所以当你点击li的时候,可以获取得到0,1,2
    }
}   

也可以改造成下面的样子

window.onload = function (){
    var oLi = document.getElementsByTagName("li");
    for (var i = 0; i < oLi.length; i++) {
        oLi[i].onclick = (function (i){
            return function(){
                    alert(i)
                };
        })(i);
    }
}   

三、闭包需要注意的地方?

3.1 IE下会引发内存泄露

内存泄露会导致浏览器内存占用过高,内存中的变量无法变量,必须关闭浏览器,关闭这个网页也无法清除

window.onload = function(){
    var oDiv = document.getElementById("div1");
    oDiv.onclick = function(){
        alert(oDiv.id); // 由于内部引用了oDiv,导致oDiv无法释放
    }
}  

可以通过onunload事件,将onclick取消,来释放oDiv

window.onload = function(){
    var oDiv = document.getElementById("div1");
    oDiv.onclick = function(){
        alert(oDiv.id); // 由于内部引用了oDiv,导致oDiv无法释放
    }
    
    window.onunload = function(){
        oDiv.onclick = null;
    }
}  

或者:

window.onload = function(){
    var oDiv = document.getElementById("div1");
    var id = oDiv.id;
    oDiv.onclick = function(){
        alert(id); // 由于内部引用了oDiv,导致oDiv无法释放
    }
    oDiv = null;
}