闭包基础

56 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 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