JavaScript闭包
闭包的简介
简单讲,闭包就是指有权访问另一个函数作用域中的变量的函数。
MDN 上面这么说:闭包是一种特殊的对象。它由两部分构成:函数,以及创建该函数的环境。环境由闭包创建时在作用域中的任何局部变量组成。
创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量
闭包的特性:
- 函数嵌套函数
- 函数内部可以引用外部的参数和变量
- 参数和变量不会被垃圾回收机制回收
闭包的优缺点
优点:
- 希望一个变量长期驻扎在内存中
- 避免全局变量的污染
- 私有成员的存在
缺点:
- 常驻内存,会增大内存使用量,使用不当很容易造成内存泄露,增大内存消耗。
function aaa() {
var a = 1;
return function(){
alert(a++)
};
}
var fun = aaa();
fun();// 1 执行后 a++,,然后a还在~ a会长期驻扎在内存中
fun();// 2
fun = null;//a被回收!!
javascript
的垃圾回收原理
(1)在javascript
中,如果一个对象不再被引用,那么这个对象就会被GC
回收;
(2)如果两个对象互相引用,而不再被第3
者所引用,那么这两个互相引用的对象也会被回收。
避免全局变量的污染
// 全局变量a 可以实现累加
var a = 1;
function aaa() {
a++;
alert(a);
}
aaa(); // 2
aaa(); // 3
// 局部变量b 不能实现累加
function bbb() {
var b = 1;
b++;
alert(b);
}
bbb(); //2
bbb(); //2
// 局部变量c 使用闭包 可以实现累加
// 减少了全局变量
var ccc = (function () {
var c = 1;
return function () { // 闭包
c++;
alert(c);
}
})
ccc(); // 2
ccc(); // 3
循环中的点击事件
<ul>
<li>111</li>
<li>222</li>
<li>333</li>
<li>444</li>
</ul>
<script>
var aLi = document.getElementsByTagName('li');
// 使用var声名 i 是全局变量,循环的i都是同一个
for (var i = 0; i < aLi.length; i++) {
aLi[i].onclick = function () { // 当点击时for循环已经结束
alert('当前点击的是第' + i + '个'); // 一直都是4
};
}
// 使用let声名 i 改写上面代码,循环的i只在本轮循环有效
for (let i = 0; i < aLi.length; i++) {
aLi[i].onclick = function () { // 每一次循环的i都是一个新的变量
alert('当前点击的是第' + i + '个');
};
}
// 使用闭包改写上面代码
for (var i = 0; i < aLi.length; i++) {
(function (i) {
aLi[i].onclick = function () {
alert('当前点击的是第' + i + '个'); // 0 1 2 3
};
})(i)
}
</script>
循环中的定时任务
for (var i = 1; i < 4; i++) {
setTimeout(function () {
console.log(i);
}, 1000); // 一直都是3
}
// 这是因为setTimeout中的匿名函数执行的时候,for循环都已经结束了
// 究其原因:i是声明在全局作用中的,定时器中的匿名函数也是执行在全局作用域中,那当然是每次都输出3了
// 我们可以让i在每次迭代的时候,都产生一个私有的作用域,在这个私有的作用域中保存当前i的值
for (var i = 1; i < 4; i++) {
(function (j) {
setTimeout(function () {
console.log(j);
}, 1000); // 1 2 3
})(i)
}
闭包的应用
1. 闭包的应用比较典型是定义模块,我们将操作函数暴露给外部,而细节隐藏在模块内部:
function module() {
var arr = [];
function add(val) {
arr.push(val);
}
function get(index) {
if (index < arr.length) {
return arr[index]
} else {
return null;
}
}
return {
add: add,
get: get
}
}
var mod1 = module();
mod1.add(1);
mod1.add(2);
mod1.add('xxx');
console.log(mod1.get(2));
2. 防抖与节流
闭包引起的内存泄漏
将不使用的局部变量全部删除,可以使变量赋值为null
//这段代码会导致内存泄露
window.onload = function () {
var el = document.getElementById("id");
el.onclick = function () {
alert(el.id);
}
}
//解决方法为
window.onload = function () {
var el = document.getElementById("id");
var id = el.id; //解除循环引用
el.onclick = function () {
alert(id);
}
el = null; // 将闭包引用的外部函数中活动对象清除
}