学习资料
学习记录
闭包是什么
从功能角度,闭包让你能通过一个内部函数(inner function)访问这个内部函数的上一层函数(outer function)的Scope。
闭包怎么用
基础使用
略过
会出现什么问题?内存泄漏
例子1:
function fun() {
let arr = Array(10000000);
return function () {
console.log(arr); // 使用了 arr
};
}
window.f = fun(); // 通过内部函数(inner function)持久存在,导致函数的上一层函数的scope(即闭包)无法被回收
例子2:
function fun() {
let arr = Array(10000000);
function fun1() {
// arr 加入 Closure
console.log(arr);
}
return function fun2() {};
}
window.f = fun(); // 即使长久持有的内部函数(inner function)不引用 arr,也会对其造成闭包无法回收
闭包原理(为什么)
简单解释
闭包是创建于外部函数(outer function),被所有内部函数(inner function)共同持有。
即 f 函数内,两个内部函数(fa,fb), fb 函数即使没用变量v1,fa函数用了,那么fb也会持有v1。所以当内部但凡有一个函数被外部环境长期持有,整个函数内被闭包的变量都会无法释放。
详细解释
let t = 111;
function fun() {
let a = 1,
b = 2,
c = 3;
function fun1() {
console.log(a);
}
fun1();
let obj = {
fun2() {
console.log(b);
},
};
}
fun();
- 前提1: 每个函数及全局都会拥有自己的
[[Scopes]]作用域。 - 看上面的代码。首先进行进行全局扫,扫到 Global -> {t, fun}
- 执行到
fun()。扫fun()-> {a, b, c, fun1, obj} - 整一个空的 Closure。暂时称为 Closure_fun
- 发现
fun1()。扫fun1()-> {a}。 - Closure_fun { a }。 a 放入 Closure_func。
fun1()的[[Scopes]]会加入一个Closure,就是 Closure_func- 发现
fun2()。扫fun2()-> {b}。 - Closure_fun { a, b }。 b 放入 Closure_func。
fun2()的[[Scopes]]会加入一个Closure,就是 Closure_func
测试一下
let theThing = null;
function replaceThing () {
let leak = theThing;
function unused() {
if (leak) {
}
}
theThing = {
longStr: new Array(1000000),
someMethod: function () {},
};
};
let index = 0;
while (index < 100) {
replaceThing();
index++;
}
- Global -> { theThing, index, replaceThing }
- theThing(1) = null // (1) 第1次赋值
- replaceThing -> { theThing, leak, unused, someMethod }
- Clousure_replaceThing(1) { leak->theThing(1)->null } // 因为 unused 引用了
- theThing(2) = {...} // (2) 第2次赋值
- theThing(2).someMethod.
[[Scopes]].Closure { leak->null } - 循环
- Clousure_replaceThing(2) { leak->theThing(2) }
- theThing(3) = {...} // (3) 第3次赋值
- theThing(3).someMethod.
[[Scopes]].Closure { leak->theThing(2) } - 循环
- ...
可以看到所有的 theThing 都在(不同的)闭包里,即使从 theThing 的角度看并没有引入上次的 theThing
总结
上面是从最实用的角度进行表述,具体V8如何进行闭包的过程是模糊表述的,我也不知道具体是什么流程。
切记闭包是本函数创建给所有内部函数共用的,基本就可以分析出具体是如何发生泄漏。