面试说“闭包”

1,070 阅读3分钟

一,闭包概念。

在一个函数内定义一个内部函数,并将内部函数返回,即使该外部函数已经执行结束了,但是内部函数引用外部函数的变量依然保存在内存中,我们就把这些变量的集合称为闭包(或者将内部函数成为闭包)。

也有些人定义闭包的概念为:在一个函数内部定义一个函数,那么这个内部函数就被称为“闭包”。

例如

function f1(){
	var a1=1;
    function f2(){
      console.log(a1)
    }
    return f2
}
let result=f1()
result();//1

如上图例子,f2被称为闭包。

当我们执行到result(); console.log(a1)这句话时,这时候的调用栈情况如下图所示

如上图所示,当我们执行到console.log时,f1的环境变量已经弹出,但是f2调用f1的变量的形成的闭包还存在。还可以继续供f2使用。

二,闭包作用

1,通过闭包,外部环境可以访问到函数里的变量。

​ 正常情况下外部环境无法访问到f1函数里变量,但是通过上诉例子,在外部调用result时是可以拿到f1里a1变量的。比如

function f1(){
	var a1=1;
    function f2(){
      return a1;
    }
    return f2
}
2,f1中被f2访问的变量,始终存在内存中。形成f2的专属变量背包。
3,使用闭包,内部函数变量不会污染外部变量,规避冲突

三,闭包的副作用

变量常驻内存,造成内存溢出,所以在不使用时,记得清空。

正常情况下,f1函数的变量在f1()执行完成退出当前执行环境时,会被垃圾回收收回。但是由于f2引用了它,而f2又赋值给了result变量,会标记f2被引用了一次,所以f2会在内存里(这里是正常的),f1中变量由于存在于f2函数的closure对象里(闭包里变量的集合)。

f2一直存在内存中,那么f2中的闭包变量也就会一直存在内存中,这些变量造成了内存溢出。

这里涉及到js垃圾回收机制,要想很好理解,可以去看看关于垃圾回收机制的问题。

解决方案:在不用时记得即使清空变量。
这里有一个小题,如果直接使用f1()()调用两次,和result()调用两次,会发现打印的a1的值是不同的。这要是一个面试题

这个原因,一是闭包,二是我上诉说的垃圾回收机制造成的。

let result=f1()将,f2函数赋值给了result,f2函数被标记了一次引用,执行完result()之后,f2还存在内存中,包括f2中的闭包变量a1,所以多次调用result(),时打印的a1++的值是从闭包变量这个作用域中取得的。这个值又常驻内存,所以会a1的值会变化。

而直接调用f1()()时,没有对f2进行赋值,虽然f2依然有闭包变量集,但是f2没有赋值,被标记次数为0,调用完就会自动清除。所以a1变量也就随着f2不见了。

真正要理解必要还是需要了解作用域和作用域链这对概念