闭包
1. 函数的词法作用域规则
基本规则:函数在执行过程中所用到的作用域是在函数定义时的作用域,而不是函数调用时的作用域。
如何理解这个规则呢?来看两个例子。
例子一:

这个例子的执行结果是
小龙。由于函数fun()不管是定义还是调用都是在同一个作用域中(如下图)。所以这无法知道函数执行过程中所依赖的作用域是哪一个作用域。

例子二:

在这个例子中,函数
fun()定义时的作用域和调用时的作用域并不在同一个作用域中(如下图)。那么执行结果会是什么?答案是小龙,而不是小鱼。我们知道
小龙是在函数fun()定义时的作用域下,而小鱼是在函数fun()调用时的作用域下,这说明函数在执行过程中所依赖的作用域是定义时的作用域。

2. 作用域链
如果在当前作用域中没有查找到所需的值,就会向上层作用域查找,直到全局作用域,这么一个查找过程形成的链条就叫做作用域链。

函数
getter()执行时会先在当前作用域中查找所需的变量,如果当前作用域没有,就会往上一层作用域查找,以此类推,直到全局作用域。如果连全局作用域都没有该变量,则会报错:xxx is not defined。上面例子中查找变量过程中形成的链条( 3 --> 2 --> 1)就是作用域链。

3. 解释闭包
- 《JavaScript高级程序设计》
- 闭包是指有权访问另一个函数作用域中的变量的函数。
- 《JavaScript权威指南》
- 从技术的角度讲,所有的函数都是闭包:它们都是对象,它们都关联到作用域链。
- 《你不知道的JavaScript》
- 当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。
个人理解闭包应该是基于词法作用域书写代码时产生的自然结果,是一种现象。
例子一:

这个例子中,函数
getter()能够获取到变量userName有两种解释:1. 作用域链 2. 闭包,所以这里不太好观察闭包这个现象。
例子二:

在这个例子中,函数
getter是在函数userInfo的词法作用域外面执行,但依然能拿到变量userName,这不就是上面所说的函数的词法作用域规则,所以闭包就是基于函数词法作用域规则下产生的一种现象。简单理解就是一个函数中嵌套另一个函数,这两个函数之间就产生了闭包。
4. 闭包作用
让外部可以获取到函数内部的局部变量
让函数内部的变量可以一直保存在内存中

这个例子就是利用了闭包实现了全局变量
getUserName对函数getter的引用,从而达到让外部获取到函数内部的变量。
疑问一:当一个函数调用结束之后,函数的本身以及内部的局部变量都会被垃圾回收机制回收。那为什么函数
userInfo调用结束之后还可以获取到其局部变量userName。
解答:原因在于
userInfo是getter的父函数,而getter被赋给了一个全局变量,这导致getter始终在内存中,而getter的存在依赖于userInfo,因此userInfo也始终存在内存中,不会在调用结束之后被垃圾回收机制回收。
5. 使用的注意点
- 由于闭包会使得函数中的变量一直保存中内存中,内存消耗很大,所以不能滥用闭包
本文详细解析JavaScript闭包,包括词法作用域规则、作用域链的概念,以及闭包的形成原理。通过实例探讨闭包如何保持局部变量持久,解释了为何函数userInfo的局部变量在调用结束后仍可访问,并强调了合理使用闭包以避免内存问题。
2万+

被折叠的 条评论
为什么被折叠?



