JS闭包的详解

一、什么是闭包?

特点:
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;
}  
posted @ 2016-11-21 11:54  hp柠檬茶  阅读(337)  评论(0编辑  收藏  举报