浅谈闭包
闭包是前端开发者必须要了解的一个知识点,说起闭包,它与变量的作用域跟变量的生命周期紧紧联系,那么我们就一起了解下闭包吧:
变量作用域
首先看个例子:1
2
3
4
5
6function fn() {
var a = 110; // a为局部变量
console.log(a); // 110
}
fn();
console.log(a); // a is not defined 外部访问不到内部的变量
上面代码就展示变量的作用域的问题,函数外部是拿不到变量a的值,那么怎么样才能拿到a的值呢?
且看下面的例子:1
2
3
4
5
6
7
8
9function fn() {
var a = 110; // a为局部变量
return function() {
return a;
}
console.log(a); // 110
}
var fn2 = fn();
var val = fn2(); // 110
这个时候函数外部val就拿到了a的值,那么到此为止,我们看见了闭包的一个意义:
闭包就是能够读取函数内部变量的函数
变量的生命周期
对于变量的生命周期分几种情况:
- 对于全局的变量来说,它的生命周期是永久的,除非我们主动干掉他,销毁他
- 对于存在于函数的中的局部变量的话,就没那么幸运了,当函数执行完毕之后,局部变量就销毁了
例如下面的代码:1
2
3
4
5function fn() {
var a = 123; // fn执行完毕后,变量a就将被销毁了
console.log(a);
}
fn();
我们再来看一段代码:1
2
3
4
5
6
7
8
9
10
11function add () {
var a = 0
return function () {
a++
console.log(a)
}
}
var fn1 = add()
fn1() // a = 1
fn1() // a = 2
在这段代码里面 局部变量a就保存下来了,并没有被销毁,正是由于闭包的缘故,返回的这个function里面还有用到a 所以a就被保存下来了
闭包的作用
可以封装私有变量,可以吧一些不需要暴露出去的私有变量封装在函数内部,这样就会减少全局变量的污染,而且也不会造成变量名冲突等等情况发生
1
2
3
4
5
6
7
8
9
10
11
12
13
14var sum = (function() {
var cache = {}; // 将cache放入函数内部,避免被其他地方修改
return function() {
var args = Array.prototype.join.call(arguments, ',');
if (args in cache) {
return cache[args];
}
var a = 0;
for (var i = 0; i < arguments.length; i++) {
a += arguments[i];
}
return cache[args] = a;
}
})();模块化
1
2
3
4
5
6
7
8(function(win, undefined) {
var a = 1;
var obj = {};
obj.fn = function() {};
// 最后把想要暴露出去的内容可以挂载到window上
win.obj = obj;
})(window);将变量的使用延长
1 | var monitor = (function() { |
闭包引起的坑
看一段经典的例子:1
2
3
4
5
6
7
8
9
10
11
12
13
14<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<script>
var aLi = document.getElementsByTagName('li');
for (var i = 0; i < aLi.length; i++) {
aLi[i].onclick = function() {
console.log(i); // ?
};
}
</script>
这里无论如何点返回的都是4,因为onclick是一个异步的操作,当点击的时候,循环都已经结束了,这时候i的值已经是4了,如果想要返回0,1,2,3,4看下面的例子1
2
3
4
5
6
7
8var aLi = document.getElementsByTagName('li');
for (var i = 0; i < aLi.length; i++) {
(function(n) { // n为对应的索引值
aLi[i].onclick = function() {
console.log(n); // 0, 1, 2, 3
};
})(i); // 这里i每循环一次都存一下,然后把0,1,2,3传给上面的形参n
}
用立即执行函数,把i的值存下来,当使用的时候就可以拿到循环当时的值
内存泄露问题
过度的使用闭包,就造成很多变量无法销毁,就会导致内存的泄露,所以当有些变量被使用完之后,如果没有存在的必要了,应当及时的释放掉,减少内存的开销,如何释放呢?直接把引用中的变量设为null 即可