很多小伙伴估计都会面试遇到被问JavaScript的闭包是什么。
在我们谈闭包之前,我们先来谈谈JavaScript的垃圾回收机制。
JavaScript 具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中使用的内存。而在 C 和 C++之类的语言中,开发人员的一项基本任务就是手工跟踪内存的使用情况,这是造成许多问题的一个根源。在编写 JavaScript 程序时,开发人员不用再关心内存使用问题,所需内存的分配以及无用内存的回收完全实现了自动管理。
这种垃圾收集机制的原理其实很简单:找出那些不再继续使用的变量,然后释放其占用的内存。为此,垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间),周期性地执行这一操作。
我们来分析一下函数中局部变量的正常生命周期。局部变量只在函数执行的过程中存在。而在这个过程中,会为局部变量在栈(或堆)内存上分配相应的空间,以便存储它们的值。然后在函数中使用这些变量,直至函数执行结束。此时,局部变量就没有存在的必要了,因此可以释放它们的内存以供将来使用。在这种情况下,很容易判断变量是否还有存在的必要;但并非所有情况下都这么容易就能得出结论。垃圾收集器必须跟踪哪个变量有用哪个变量没用,对于不再有用的变量打上标记,以备将来收回其占用的内存。用于标识无用变量的策略可能会因实现而异,但具体到浏览器中的实现,则通常有两个策略:标记清除,引用计数。
从以上的字面意思可以理解为:全局变量是不会被JavaScript垃圾回收机制给回收。
而局部变量是:如果一直被引用,则不会立刻对该变量进行回收,直到给变量没有被引用后,回收机制开始对该变量进行回收处理。
那么什么是闭包呢?
闭包是指有权访问另一个
函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。
function a() { var x = 5 return function (y) { return x+y } } var result = a() console.log(result(5)); //10
以上代码运行结果为10,好像没什么问题,其实这个时候a函数就发生了闭包,为什么呢?
我们来捋一捋JavaScript对以上代码是怎么执行的。
先声明了一个函数a,声明了一个变量result。
然后result赋值的时候调用了a函数。
a函数开始执行,在a函数内部创建了一个变量x,然后给这个x赋值,最后返回一个匿名函数。
匿名函数就赋值给了result。
最后打印的时候调用了这个匿名函数,并且给这个函数传了参数。
然后开始执行这个匿名函数,因为匿名函数引用了外部函数a里的局部变量x,所以在调用a函数的时候并没有立即释放这个x函数变量。
所以我们得到了x+传进来的5,为10。
代码结束。
结束之后我们是不是发现了一个问题,函数a里的局部变量x没有被垃圾回收,而是保存了下来。
这就是JavaScript的闭包,这个闭包是发生在a函数。
我们可以打开浏览器的调试工具(F12)sources这一栏里设置一个断点,我们来看看到底发送了啥。
1.定义一个result 赋值的时候调用了a 进入了 var x=5这一步
2.给x赋值之后 开始返一个函数,返回的这个函数里引用了局部变量x。
3.返回之后等到了一个函数 并赋值给result
4.打印的时候,我们调用result这个函数并且传一个参数为5,然后开始执行函数,又会回到a函数里的匿名函数,开始执行return x+y
5.输出结果 10。结束之后,我们仍然可以看到result里发现函数a的变量x还是没有被回收。