闭包是什么?(官方文档解释)
闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建。
换成代码,可能更容易理解:
var i =10;
function a(){
var j =20;
var k =30;
function b(){
var l =40;
function c(){
console.log(i,j,k,l); // 10,20,30,40
}
c();
}
b();
}
a();
console.log(j) // 报错 j is not defined
在上面的代码中,函数 c 中一个变量都没有创建,却要打印 i、j、k 和 l,
这些变量分别存在于 函数a、b 以及全局作用域中,因此创建了 3 个闭包,
全局闭包里面存储了 i 的值,闭包 a 中存储了变量 j 和 k 的值,闭包 b 中存储了变量 l 的值。
执行代码后打印出结果 10 20 30 40 ,而最后一句打印代码则是报错 j is not defined
结论:
闭包是指在定义函数时,周围环境中的信息可以在函数中使用。或者说,在执行函数时,只要在函数中使用了外部的数据,就创建了闭包,而作用域链,是实现闭包的手段。
实际上,如果是自动形成的闭包,是会被销毁掉的。如最后一句打印代码则是报错 j is not defined,此时已经没有任何闭包存在,因为垃圾回收器会自动回收没有引用的变量,不会有任何内存占用的情况。
有时我们需要根据需求手动的来制造一个闭包,如下代码:
function eat(){
var food = '白菜';
return function(){
console.log(food);
}
}
var a = eat();
a(); // 白菜
代码中eat函数向外部返回了 eat 内部的匿名函数,而这个匿名函数有引用了 food,所以垃圾回收器是不会对其进行回收的,这也是为什么在外面调用这个匿名函数时,仍然能够打印出 food 变量的值。
结论:
-
通过闭包可以让外部环境访问到函数内部的局部变量。
-
通过闭包可以让局部变量持续保存下来,不随着它的上下文环境一起销毁。
闭包总结
1.闭包是一个封闭的空间,里面存储了在其他地方会引用到的该作用域的值,在 JavaScript 中是通过作用域链来实现的闭包。
2.只要在函数中使用了外部的数据,就创建了闭包,这种情况下所创建的闭包,垃圾回收器会自动回收,释放占用的内存空间。
3.我们可以通过一些手段手动创建闭包,从而让外部环境访问到函数内部的局部变量,让局部变量持续保存下来,不随着它的上下文环境一起销毁。
4.手动创建的闭包,可以把被引用的变量设置为 null,即手动清除变量,这样下次 JavaScript 垃圾回收器在进行垃圾回收时,就会把设为 null 的变量回收。