掘金评论:本质就是上级作用域内变量的生命周期,因为被下级作用域内引用,而没有被释放。就导致上级作用域内的变量,等到下级作用域执行完以后才正常得到释放
任何闭包的使用场景都离不开这两点:
- 创建私有变量
- 延长变量的生命周期
一般函数的词法环境在函数返回后就被销毁,但是闭包会保存对创建时所在词法环境的引用,即便创建时所在的执行上下文被销毁,但创建时所在词法环境依然存在,以达到延长变量的生命周期的目的
举个例子:
var data = [];
for (var i = 0; i < 3; i++) {
data[i] = function () {
console.log(i);
};
}
data[0]();
data[1]();
data[2]();
// 3 3 3
当执行到data0的时候,上面的for循环已经执行完成了,i是全局变量这个时候打印的i已经是3了。
data[0]其实就是一个打印 i 的函数
for (var i = 0; i < 3; i++) {}
console.log(i) // 3
再看看闭包版本
var data = [];
for (var i = 0; i < 3; i++) {
data[i] = (function (i) {
return function(){
console.log(i);
}
})(i);
}
data[0]();
data[1]();
data[2]();
// 1 2 3
下面是个人理解
var data = [];
for (var i = 0; i < 3; i++) {
data[i] = (function (i) {
return function(){
console.log(i);
}
})(i);
}
data[0]();
data[1]();
data[2]();
// 这时候data[0]就相当于
(function (i) {
// 这里的i是0
return function(){
console.log(i);
}
})(0)
// 实际上这里应该有两个i 一个是for循环的时候 用var 声明的全局变量i这个i的值等于3,也就是第一段函数的例子当函数执行console.log(i);只有全局作用域找到了全局变量i,这个i等于3 所以输出的值是3
// 第二个i是自执行函数内部的i,在data[0]的时候因为当时循环传的参数是0,所以自执行函数内部的i也是0,当执行console.log(i);时向上查找i先遇到的是自执行函数的函数作用域,这里的i等于0,就直接打印了0,当然在这个时候外部全局变量的i也是3,只是因为被函数作用域的i截胡了所以没有打印3
// data[1] data[2] 同理