【随记】闭包它到底是个啥呢?

346 阅读4分钟

闭包和原型链,我认为是js中必较难的两大痛点,在之前的文章里我大致的介绍了一下原型链这个东西,那么今天就再来解决一下闭包。目前还是青铜阶段,文章也只是帮助自己加深印象,如果同时能帮助到其他同学就更好了。多多包涵。

一、什么是闭包?

1. 闭包是嵌套的内部函数

/**
这个其实大部分同学都能够理解也知到,说白了,就是一个函数套着一个函数,像这样:
*/
function fn1(){           // 一个外部函数fn1
    var a = 1
    var b = 1            
    function fn2(){       // 一个内部函数fn2
        console.log(a)    // 实际上,在执行函数定义时,闭包就已经产生了,不用调用fn1
                          //只有调用外部函数时才会产生新的闭包
    }
}
fn1()

2. 包含被引用的变量(函数)的对象

image.png (图1)

/**
  还是这段代码,通过观察我们会发现,内部函数fn2中实际上需要引用变量a来打印输出。  
但a变量只存在于外部函数fn1中,所以此时就需要一个东西来包含那个所需要的变量a好让  
log打印输出。通过chrome浏览器的开发者工具就可以看到(`图1`)一个名为Closure的这么  
一个东西,其实他就是闭包,可以把他理解成一个对象。
*/
function fn1(){           // 一个外部函数fn1
    var a = 1
    var b = 1            
    function fn2(){       // 一个内部函数fn2
        console.log(a)    // 实际上,在执行函数定义时,闭包就已经产生了,不用调用fn1
    }
}
fn1()
//注意!  闭包存在于嵌套的内部函数之中!!!!!!!!!

二、闭包产生的条件

  在了解了闭包是什么之后,还有一个问题就是闭包是如何产生的呢?或者说闭包产生的条件是什么?在这里我总结了以下几点。

1. 函数的嵌套

这个其实在上面也已说过了,想要产生一个闭包,首先就是要有函数的嵌套。 2. 函数引用了外部函数的数据(变量/函数) 这个也就是说,当一个嵌套的,内部函数(子)引用了嵌套的,外部函数(父)时,就产生了闭包。

这里大家可以注意以下,在图1中,闭包Closure中,并没有变量b,这就是因为在fn2中没有引用外部函数的变量b。如果a也没有引用,那么闭包就不会产生!

没引用a image.png

引用了a image.png

三、闭包的作用

再思考作用前,我们可以考虑以下两个问题:

  1. 函数执行完毕之后,函数内部声明的局部变量是否还存在?
  2. 在函数外部能否直接访问函数内部的变量?

   先来说一下一。一个变量,他的生命再调用时产生,在结束时消亡。当一个函数执行完毕之后,内部的变量也会随之消失,所以说我们如果想要去操作这个变量是很困难的。
再说一下二。根据作用域链我们知到,如果一个函数内部没有这个变量,他就会去向外找查,有则返回该值,没有则返回underfined,所以说我们在函数外部是无法看到函数内部的变量的,更不用说去操作了。
为了解决这两个问题,于是就有了闭包。

作用一、 使函数内部的变量在函数执行后,任然存活在内存空间中(相当于延长了局部变量的生命周期)
作用二、 可以让外部函数操作(读/写)到函数内部的数据(变量/函数)

function fn1(){           // 一个外部函数fn1
    var a = 1
    var b = 1            
    function fn2(){        // 一个内部函数fn2
        a++
        console.log(a)    // 实际上,在执行函数定义时,闭包就已经产生了,不用调用fn1
    }
    return fn2
}
 var ff = fn1()
 ff()
 
 2   //输出a的值 2

在这里,变量a实际上是要在调用fn1结束后要消亡的,但是由于ff()的调用,是的函数fn2是要去调用打印a的,所以这样操作实际上就是延缓了a的消亡时间,同时又拿到了在函数内部变量a的值。实现了读取函数内部的变量的操作。