什么是闭包
闭包是函数可以保留和访问其外部变量,比如
let a = 1
let b = function() {
console.log(a)
}
这里变量b指向的函数可以访问外面的变量,你会说这不是废话吗?函数都可以访问外部变量呀
那再看一个例子
function f() {
let value = 123;
return function() {
alert(value);
}
}
let g = f();
这里函数f执行后,value值是不会被回收的,因为变量g有对函数f的返回有个引用,这个返回是个函数,函数里用到了value,这里也是个闭包。
通过闭包可以保留外部变量的特性,可以实现一些功能,比如
function add() {
let value = 0
return function(v) {
value += v
return value
}
}
let a = add()
console.log(a(1)) // 1
console.log(a(2)) // 3
闭包造成的内存泄漏
先看看这段代码
let theThing = null;
let replaceThing = function () {
let leak = theThing;
let unused = function () {
if (leak)
console.log("hi")
};
theThing = {
longStr: new Array(1000000),
someMethod: function () {
console.log('a');
}
};
};
这里如果replaceThing执行了多次,就会造成内存泄漏。一开始我也没搞懂,这里不就最多同时存在两份大内存吗?就是当次的theThing变量和leak变量指向上次的theThing变量,没有形成一条引用链呀?
你要说someMethod方法里,如果用到了leak,那someMethod有对leak的引用,然后当下次replaceThing执行时,新的leak指向了上次的theThing,上次的theThing的someMethod里又引用了旧的leak,这样就形成了leak->someMethod->leak->someMethod… 的引用链。
通过chrome的devtools的内存分析工具也能看到这个引用链
但someMethod里没有对leak的引用呀?
这里就是闭包形成的内存泄漏,someMethod指向的函数能访问外部变量,这里就包括了leak,哪怕他内部没有用到leak,但他所处的环境却用到了。
你会说unused都没用到,不是会被引擎优化掉吗?甚至都没这个变量了。
是的unused被优化掉了。
但函数的定义还是有的,函数里用到leak,所以leak变量没被优化。那这个函数连名字都没用呀,都调用不了,怎么不顺便优化了?
这里是没理解的,不过这种情况实际上更早就能发现
ts就报错了。
但浏览器认为leak有用到就说明这个匿名还是还是被定义了,难道是漏优化了?可能之后这个case就不会内存泄漏了。有大佬知道咋回事吗?
参考资料
- 变量作用域,闭包
- 函数声明提前