闭包——Javascript 进阶知识整理

时间:2022-04-13 22:39:28

闭包:所谓闭包,是指一个函数A内部定义了另外一个函数B(函数的嵌套定义),当B在执行时可以使用A中定义的变量。

  1. 闭包的使用场景
    闭包可以用在很多地方,一般在定义了变量,然后又定义一个函数,其中使用了刚刚定义的变量的时候,我们就创建的一个闭包。这样的场景常常出现在DOM操作,事件监听等需要将一个函数作为回调函数,等待时机执行的时候。

  2. 闭包的原理
    我们假定有一个函数A,在A函数内部,我们声明了函数B,形成一个闭包。因为B函数仅仅只是在A中定义,执行的时机其实并不是在A执行的时候。B的引用会被一个外部对象持有,例如,如果B是一个事件监听的处理函数的话,B的引用会被浏览器处理事件监听的对象拿到,等到事件产生,调用B;定时器、AJAX等都是类似的流程。这里的关键就是闭包内的函数B的引用被另一个在A之外的对象所持有,不一定什么时候B会被调用。伴随着这种使用场景,就会引来一个问题:如果A执行完了(不存在A没有执行的情况,因为这时闭包的B不存在,对B的增加事件监听等的操作也不存在),过了好长时间外部环境发现要执行B函数了,通过B函数在外部的引用调用了B,这里的疑问是A已经执行完了,A内所声明的变量按理说都会销毁掉,不会再保留,而B函数实际是引用了A中声明的变量的,甚至会操作这些变量,那么B能成功引用到这些变量,执行成功吗?
    当然可以,不然闭包还有个什么用。在处理这个问题是,A函数会被保留下来,从垃圾回收的角度来说,A函数内还有变量的引用被外部环境所持有,不满足垃圾回收销毁对象的要求(垃圾回收会销毁不再被使用的对象,清理内存,这里‘不再别使用的对象’的标准就是外部环境不持有对这个对象的引用),所以A函数就不会被销毁,进而A内所有的变量都保留了下来供B函数使用。但是,保留下来的仅仅是A内的各种变量(B函数的执行环境),A函数因为外部不再持有引用,已经不可达了(无法引用A,或者调用A)。这样形成了一个比较奇怪的领域,在这个领域内的变量只能通过内部暴露出来的函数B进行访问,而不能直接访问,相当于形成了一个保护罩。

  3. 闭包和即时函数的配合
    如果我们本来的目的就是要使用B,同时保护B的执行环境,但是我们并不关心A,这种需求下,闭包可以如何简化呢?
    可以使用即时执行函数来做简化,通过(function(){})()的形式,在即时执行函数内使用一个匿名函数代替A,这样,这个函数声明之后立即就会执行了,匿名函数中B的引用也(通过匿名函数内的操作)成功被挂到了其他外部对象上,减少了A函数的声明,同时使用闭包保护了匿名函数内的变量不会被外部非法访问。