什么是闭包
两个例子:
例子1
function bar(){
console.log(a);
}
function fn() {
var a=100
bar()
}
var a=200
fn()
// 200
例子2
function fn2() {
var a=100
function bar2(){
console.log(a);
}
bar()
}
var a=200
fn2()
// 100
这两个例子区别是 bar
函数声明在外部,bar2
函数声明在了 fn2
的内部,所以很明显例子2是形成了一个闭包。
在例子1中,fn()
函数已经执行完了,在调用栈里被删除了,所以里面的a不会被访问到,当 bar()
函数要输出a
但是其内部没有a时,就会去到它的 outer
的词法作用域中寻找 a=200,所以最终输出 200
再看例子2,外部函数声明了 foo
函数,和a变量 ,在 foo
函数内部,bar
函数被声明了。按理说,fn2
函数执行完,内部的a应该被回收掉,但是bar
函数输出的a却是100。因为内部函数引用了外部函数中的变量,所以内部函数没有被释放,依旧被保存在了内存中。
简单来说就是:一个内部函数访问了其外部函数的变量时,形成了闭包。
再来一个例子
function fn3() {
var a=100
function bar3(){
console.log(a);
}
return bar3
}
var a=200
var myFn3 = fn3();
myFn3();
//100
这段代码的效果和之前 fn2
函数的示例完全一样。不同在于内部函数 bar3
在执行前,从fn3
外部函数返回了。
当通过调用一个外部函数返回的一个内部函数后,即使外部函数执行已经结束了,但是内部函数引用了外部函数中的变量也依旧需要被保存在内存中,所以 bar3
依旧能够访问到fn3
中定义的变量的值并输出
闭包的应用
闭包可以将变量封装在函数内部,保护变量不被外部访问和修改,实现数据的私有。
有效减少全局作用域的污染,保持代码的整洁和可维护性。
可以存储计算结果,避免重复计算,提高程序效率。
var makeCounter = function () {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function () {
changeBy(1);
},
decrement: function () {
changeBy(-1);
},
value: function () {
return privateCounter;
},
};
};
var Counter1 = makeCounter();
var Counter2 = makeCounter();
console.log(Counter1.value()); /* logs 0 */
Counter1.increment();
Counter1.increment();
console.log(Counter1.value()); /* logs 2 */
Counter1.decrement();
console.log(Counter1.value()); /* logs 1 */
console.log(Counter2.value()); /* logs 0 */
每次调用其中一个计数器时,通过改变这个变量的值,会改变这个闭包的词法环境。然而在一个闭包内对变量的修改,不会影响到另外一个闭包中的变量。
闭包的缺陷
除特定情况下,尽量减少闭包的使用,因为闭包可能会导致这些变量及相关的整个作用域长时间驻留在内存中,从而引发内存泄漏。
如果是大量使用闭包或者循环中创建闭包的情况下,必须谨慎处理,确保不再使用的变量能够适时释放,避免不必要的内存占用。
消除闭包
解除 bar
对外部函数变量的引用,回收相应的内存空间,避免不必要的内存占用。
function fn() {
var a=100
function bar(){
console.log(a);
}
return bar
}
var a=200
var myFn = fn();
myFn();
myFn = null