闭包、内存释放机制
let x = 5;
function fn(x) {
return function(y) {
console.log(y + (++x));
}
}
let f = fn(6);// x = 6 执行fn函数所形成的的执行上下文不销毁,因为它里面创建的私有变量被外界的f占用了
f(7); // y=7; y+(++x) => 7+7=14
fn(8)(9); // => x=8, y=9 y+(++x) =>9+9=18
f(10); // y=10, x=7 y+(++x) => 10+8=18
console.log(x); // 5
/*
* 解析:本来画图更容易理解的,但是我这里不好画图,大家将就看哈
全局作用域 EC(G)
变量提升:
fn = AF0 [[scope]]: EC(G)(函数是引用型,创建了堆内存,引用的是堆内存的地址)
代码执行:
x = 5;
f = fn(6);
fn(6)执行:形成一个新的执行上下文 EC(f1)
初始化作用域链:
[[scope-chain]] = <EC(f1), EC(G)>
形参赋值:
x = 6;
执行完f(7)之后 x = 7
执行完f(10)之后 x = 8
代码执行:
return function(y){...}
等价于返回一个堆内存地址,return AF1
这个执行上下文中有私有变量AF1被外界f占用,所以不出栈销毁
f = fn(6) === f=AF1
f(7)执行:等价于AF1(7)执行 => 形成一个新的执行上下文 EC(f2)
初始化作用域链:
[[scope-chain]]: <EC(f2), EC(f1)>
形参赋值:
y = 7;
代码执行:
console.log(y+(++x)); y是私有的,值为7,x不是私有的,沿着作用域链查找,在EC(f1)中有x=6, ++x => x=7
y+(++x) =>7+7 = 14
这个执行上下文没有私有变量被外界引用,所以出栈销毁
fn(8)(9); 分两步:先执行fn(8),再把其返回的结果执行并传参9
fn(8)执行:形成一个新的执行上下文 EC(f3)
初始化作用域链:
[[scope-chain]] = <EC(f3), ED(G)>
形参赋值:
x = 8;
代码执行:
return function(y){...} [[scope]]: EC(f1)
=> return AF2 这里的解释跟fn(6)执行逻辑一样
这个执行上下文中有私有变量要在全局接着执行,所以暂时不出栈销毁
AF2(9)执行:形成一个新的执行上下文 EC(f4)
初始化作用域链:
[[scope-chain]] = <EC(f4), EC(f3)>
形参赋值:
y = 9;