什么是闭包?从底层解释闭包的运行

之前看过廖雪峰老师的闭包讲解,当时不明白其中的运行原理,今天在看了渡一的js课程后,明白了闭包的原理。那就举两个例子来解释下。

首先要了解预编译中的几个名词:

GO对象(Global Object),全局环境下创建的执行期上下文,就是全局作用域。
AO对象(Activation Object),也叫执行期上下文。在函数被执行前一刻创建,可以称为是局部环境、局部作用域。
Scope chain(作用域链),被创建的局部作用域会继承父级的作用域,形成一条作用域链。

再记住一点

但凡是内部的函数被保存到了外部它一定生成闭包。内部函数会保存外部函数的劳动成果,保存了还没执行,被放到了外部,而外部的函数自身AO就被内部的闭包函数保存了。

在一个函数的执行前有个预编译,步骤如下
1.创建AO对象(Activation Object),执行期上下文。
2.寻找函数的形参和内部的变量声明,将变量声明和形参名作为AO属性名,如果有重复则跳过,初始值为undefined。
3.将实参的值放到形参里面去,将实参值和形参统一。函数内变量声明的值传递到AO对应的属性名。
4.在函数体内部寻找函数声明,创建AO属性名,属性值为函数体。
5.GO除了没第三步,其它都和AO一样。
接下来看廖雪峰老师,讲python闭包那节课中的两个例子

def count():
    fs = [ ]
    for i in range(1, 4):  
        def f():                             
             return i*i
        fs.append(f)
    return fs

f1, f2, f3 = count()
--------------------------------
>>> f1()
9
>>> f2()
9
>>> f3()
9

按上面的理论来看,在for循环结束后,原本的fs = [ ]从一个空数组变成了fs = [ f , f , f ],里面是一样的函数名。
并且在循环体结束后,i = 3。
此时 f1, f2, f3 = count(),所继承的东西是一样的,都是函数名 f。
因为函数b被return抛出来了,它带着a的AO和GO,而在a的AO里i = 3。
所以在执行f1, f2, f3时,结果就都等于9了。

然后看第二个例子:

def count():
    def f(j):
        def g():
            return j*j
        return g
    fs = []
    for i in range(1, 4):
        fs.append(f(i))   # f(i)立刻被执行,因此i的当前值被传入f()
    return fs
    
f1, f2, f3 = count()    
---------------------------------
>>> f1()
1
>>> f2()
4
>>> f3()
9

在count的AO里,空数组从fs =[ ]变成了fs = [ f(1) , f(2) , f(3) ]。
先不继续将函数f,g的AO列出来。
直接从return fs开始。这时候f1, f2, f3从数组中被赋予了三个不同的函数,这三个函数是以数组的形式,被闭包返回了出来,这三个函数都得到了count的AO和GO

当执行f1, f2, f3时,其实执行的是被返回的g,函数g执行的就是j * j这个表达式。
而由于给数组末尾添加的是f (i),也就是 f(1) , f(2) , f(3) ,将实参替换形参,就得出了1,4,9的结果。

底层的运行大概就是这样了,但还有很多东西没有写出来,闭包最重要的就是弄明白AO和GO的运行步骤、作用域链继承,这些懂了,闭包就没问题了。

闭包的作用:
1.实现共有变量。
2.可以用作缓存。
3.可以实现函数封装,及属性私有化。

猜你喜欢

转载自blog.csdn.net/qq_28766729/article/details/83447943