一、闭包的特点
1.在函数内部嵌套函数;
2.内部函数使用外部函数的参数和变量
3.重点:函数内部的变量(函数参数)不会被浏览器的垃圾回收机制回收,闭包可以使得它的诞生环境一直存在。
闭包使得函数内部环境一直存在,除非非手动销毁,将值设为null;
简单的理解:
闭包函数就是一个声明在函数内部的函数;
闭包就是内部函数总是可以访问外部函数的参数和变量,并且在外部函数被返回后仍然可以访问;
综合理解:闭包就是可以创建一个独立的环境(在内存中),每个闭包里面的环境都是独立的,互不干扰。闭包会发生内存泄漏,每次外部函数执行的时 候,外部函数的引用地址不同(将外部函数赋值给一个外部变量或者叫活动对象),都会重新创建一个新的地址。只要当前活动对象(外部变量)中有被内部子集(内部指函数内部)引用的数据,那么这个时候,这个数据不删除,保留一根指针给内部活动对象。
例子:这是一段闭包改造后的自执行函数。 1.我们可以看见因为外部类数组 list 的子项被 for 循环内的函数内部子集调用了,每一次迭代都给 list 对应的子集在内存中开辟了一块独立区域存放参数 i 。 2.所以在 for 循环的迭代过程中,每一次迭代的结果 i 都被一个 list 子项在内存中保存下来。 3.在我们没有进行点击事件之前,代码最下方的打印会先打印一个4,因为点击事件相当于是一个异步函数,所以先把 for 循环的最终迭代结果打印出来。 4.在我们点击每个 list 子项时,会将每个 list 子项在内存中所保存的参数打印出来。
var list = document.querySelectorAll('li'); //4个li
for (var i = 0; i < list.length; i++) { //i:0,1,2,3
(function (i) { //i是参数,相当于函数内部的变量,形成闭包,i一直存在。i:0,1,2,3
list[i].onclick = function () {
console.log(i); // 0,1,2,3
}
})(i); //i:0,1,2,3
}
console.log(i); //4,循环的最后一次值。
循环和定时器的问题
for (var i = 1; i <= 5; i++) {
setTimeout(function() {
console.log('hehe'); //定时器会被执行5次
}, 1000);
}
for (var i = 1; i <= 5; i++) {
setTimeout(function() {
console.log(i); //定时器会被执行5次 异步:等到循环执行完成。 这里的值是6,因为定时器异步执行,而循环的执行顺序在定时器前,所以优先循环完5次定时器后,等定时器读取 i 的值的时候 i 就已经是6了
}, 1000);
}
改为闭包实现
for (var i = 1; i <= 5; i++) { // i:1,2,3,4,5
! function(i) { //i:i是参数,相当于每一轮循环的 i 的值保存进这个自循环函数中了,循环了五次就相当于创建了五个闭包,保存了五个 i 的值
setTimeout(function() {
console.log(i); //1,2,3,4,5
}, 1000);
}(i); //1,2,3,4,5
}
闭包的优缺点—面试题
1.闭包的好处:
希望一个变量长期存储在内存中(延长变量作用域) - 循环添加事件,里面的循环变量(循环里面使用定时器)
避免全局变量的污染,局部变量(函数内部的变量)的存在。
2.闭包的缺点:
2.1.闭包使得内部的变量一直存在,变量常驻内存,增加内存使用量—手动销毁,变量的值设为null
2.2.使用不当会造成内存泄漏
内存泄露是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束。
当变量标记为离开环境或者变量引用计数为0,而垃圾回收机制无法回收时,就会产生内存泄漏。
经典面试题
var name = "The Window"; // var声明的变量也是window下面的属性。
var object = {
name:"My Object",
getNameFunc:function() { //this->object
return function() {
return this.name; //this->window,不是object直接调用,里面的方法返回的函数表达式直接使用。
};
}
};
alert(object.getNameFunc()); //返回的是getNameFunc内部的函数体
alert(object.getNameFunc()()); //The Window
var name = "The Window";
var object = {
name:"My Object",
getNameFunc:function() { //this->object
var that = this; //将指向object的this赋值给that,that指向object的this
return function() {
return that.name;
};
}
};
alert(object.getNameFunc()()); //My Object