开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第27天,点击查看活动详情
说闭包之前我们先看一下,函数的执行环境
function a() {
function b() {
function c() {
}
c();
}
b();
}
a();
// a定义:a.[[scope]] -> 0 : 60
// a执行:a.[[scope]] -> 0 : a -> AO
// 1 : GO
// b定义:b.[[scope]] -> 0 : a -> AO
// 1 : GO
// b执行:b.[[scope]] -> 0 : b -> AO
// 1 : a -> AO
// 2 : GO
// c定义:c.[[scope]] -> 0 : b -> AO
// 1 : a -> AO
// 2 : GO
// c执行:c.[[scope]] -> 0 : c -> AO
// 1 : b -> AO
// 2 : a -> AO
// 3 : GO
// c结束:c.[[scope]] -> 1 : b -> AO
// 2 : a -> AO
// 3 : GO
// b结束:b.[[scope]] -> 0 : a -> AO
// 1 : GO
// c.[[scope]] 结束
// a结束:c.[[scope]] -> 0 : GO
// b.[[scope]] 结束
闭包
function test1() {
function test2() {
var b = 2;
console.log(a);
}
var a = 1;
return test2();
}
var c = 3;
var test3 = test1();
test3();
//我们可以得出 输出a为1
//test3执行,test2的作用域链增加自己的AO,当打印a的时候,在自己的AO上没有查找到,则向test1的AO查找。
//再次执行test3时,实际操作的仍然是原来test1的Ao
//当test3执行结束后,test2的AO被销毁,但原来test1的AO仍然存在且被test2连着
当内部函数被返回到外部并保存时,一定会产生闭包,闭包会产生原来的作用域链不释放,过渡的闭包可能会导致内存泄漏,或加载过慢
举例
function test1() {};
var c = 3;
var test3 = test1();
//当test1函数被定义时,系统生成I[scope]]属性,[[scope]]保存该函数的作用域链,
//该作用域链的第0位存储当前环境下的全局执行期上下文GO,GO里存储全局下的所有对象,其中包含函数test1和全局变量c
function test1() {
function test2() {}
var a = 1;
return test2;
};
var c = 3;
var test3 = test1();
//当test1函数被执行时 (前一刻),函数test2被定义
function test1() {
function test2() {}
var a = 1;
return test2;
};
var c = 3;
var test3 = test1()
test3();
//当test1函数被执行结束时,因为test2被返回到外部,且被全局变量test3接收。这是test1的AO并没有销毁,只是把线剪断了,test2的作用域继还连着的
function test1() {
function test2() {
var b = 2;
console.log(a)
}
var a = 1;
return test2;
};
var c = 3;
var test3 = test1()
test3();
//test3执行,test2的作用域链增加自己的AO,当打印a的时候,在自己的AO上没有查找到,则向test1的AO查找。再次执行test3时,实际操作的仍然是原来test1的AO