闭包
概念
一个外部函数里面套一个内部函数,内部函数调用外部函数的局部变量,当外部函数执行完之后,变量不会释放;
闭包的好处
- 可以间接调用函数内部的局部变量。
- 可以让这些变量的值始终保持在内存中。(因此要注意不能滥用闭包)
- 可以暂存数据,给变量开辟私密空间,避免外部污染。
闭包的坏处
- 内存消耗
- 性能问题
通常来说,函数的活动对象会随着执行期上下文一起销毁,但是,由于闭包引用另外一个函数的活动对象,因此这个活动对象无法被销毁,这意味着,闭包比一般的函数需要更多的内存消耗。尤其在IE浏览器中需要关注。由于IE使用非原生javascript对象实现DOM对象,因此闭包会导致内存泄露问题
使用闭包时,会涉及到跨作用域访问,每次访问都会导致性能损失。
因此在脚本中,最好小心使用闭包,它同时会涉及到内存和速度问题。不过我们可以通过把跨作用域变量存储在局部变量中,然后直接访问局部变量,来减轻对执行速度
的影响
闭包存在的坑
- 坑一:内存泄漏。比如要为某个元素添加onclick事件的时候
function showId () {
var el = document.getElementById('test');
el.onclick = function () {
alert(el.id);
}
}
这样写会造成内存泄露,换成下面的写法:
function showId () {
var el = document.getElementById('test');
var id = el.id;
el.onclick = function () {
alert(id); //这样写会导致id去访问外部函数作用域的id,造成内存泄漏
}
el = null;
}
- this的指向问题
var object = {
name: '对象',
getName: function () {
return function (){
console.log(this.name);
}
}
}
object.getName()();//打印啥?
打印出来的居然是undefined,第一个括号结束过后是一个函数,你调用了一个全局函数而已嘛
- 坑3:引用的变量可能发生变化。(最常见的,以前分享提到过)
function outer(){
var result = [];
for(var i=0;i<10;i++) {
result[i] = function (){
alert(i);
}
}
return result;
}
outer().forEach(fun => {
fun();
})
这样会打印出10个10,而不是09,要把他变成立即执行函数才会变成09。因为每次function不是立即执行,得到的是一个序列,等到执行函数时,变量已经迭代到10,所以引用的值变成了10
修改
function outer(){
var result = [];
for(var i=0;i<10;i++) {
result[i] = function (){
alert(i);
}(i)
}
return result;
}
outer().forEach(fun => {
fun();
});
应用
<ul id="ul1">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
</ul>
<script>
var oUl = document.getElementById('ul1');
var aLi = oUl.getElementsByTagName('li');
for (var i = 0; i < aLi.length; i++) {
(function (index) {
aLi[index].onclick = function () {
alert(index)
}
})(i);
}
// es6
for(let i = 0; i < aLi.length; i++) {
aLi[i].onclick = function () {
console.log('ES6-' + i)
}
}
// 非闭包的方式
for(var i = 0; i < aLi.length; i++) {
aLi[i].index = i;
aLi[i].onclick = function () {
alert(this.index)
}
}
// jq实现
$(document).ready(function () {
$("#ul1 li").click(function () {
alert($(this).index());
})
})
</script>
{/* // 利用闭包,每次调用都自增1,立即调用函数 */}
var add = (function () {
// 声明一变量,由于下面 return所以变量只会声明一次
var count = 0;
return function () {
return console.log(count++);
};
})();
add(); // 0
add(); // 1
add(); // 2