likes
comments
collection
share

还在觉得闭包难懂?看这里!没问题!

作者站长头像
站长
· 阅读数 29

前言

闭包是指在 JavaScript 中,内部函数可以访问外部函数作用域中的变量和参数,即使外部函数已经执行完毕也可以访问。闭包是通过作用域链的机制实现的。

具体来说,当内部函数被定义时,它会记住其外部函数的作用域链。即使外部函数已经执行完毕,内部函数依然可以访问外部函数作用域中的变量和参数。这种特性使得 JavaScript 中的函数可以形成闭包。

闭包在 JavaScript 中有多种用途,其中包括:

  1. 创建私有变量:通过闭包可以创建私有变量,使得外部无法直接访问,从而实现封装和信息隐藏。
  2. 延迟执行函数:闭包可以将函数作为参数传递给其他函数,从而延迟执行函数。
  3. 保存状态:闭包可以保存函数执行时的状态,例如计数器或迭代器。
  4. 解决异步操作中的问题:闭包可以用于解决异步操作中的作用域问题,例如在事件处理程序中访问外部变量。

需要注意的是,闭包可能会导致内存泄漏问题,因为闭包会持有外部函数作用域中的变量,使得这些变量无法被垃圾回收。因此,在使用闭包时需要注意内存管理,避免不必要的资源消耗。

作用域链的加深理解

我们之前已经花了一篇文章来说明了作用域链的规则,那么今天让我们再次探讨一下作用域链以加深对它的理解。

还在觉得闭包难懂?看这里!没问题!

那么这道题的输出会是多少呢?

还在觉得闭包难懂?看这里!没问题!

我们对这道题依然可以用之前的方法去做

还在觉得闭包难懂?看这里!没问题!

步骤

  1. 创建全局执行上下文,里面包括变量环境和词法环境,包括两个函数和myName.
  2. 然后开始执行全局上下文,碰见了函数调用需要形成函数foo执行上下文。
  3. 同理,包括myName,里面的outer指向所在的词法作用域(函数发挥作用的区域,也可以理解为函数所在上一级区域的作用域),这里指向全局。
  4. 接着调用bar函数,同理创建bar执行上下文。
  5. 最后bar里的outer指向全文(因为词法作用域只和函数的声明有关,和函数的调用位置无关),因为在全局声明,所以最后打印的时候自己区域没有,便往全局找,最终打印jerry。

这道题是对作用域链的整个总结,会大大加深我们对作用域链的理解。

闭包

let和{}形成块级作用域

我们再次抛出一道题

还在觉得闭包难懂?看这里!没问题!

那么当我们打印这个数组的时候,这个数组的输出会是什么呢,结果是10个10,为什么达不到我们想要的效果,0-9呢?因为我们的i是用var声明的,是一个全局变量,我们想象一下,首先把10个函数放入数组,然后肯定不会执行,最后执行的时候,首先函数里面不会有i因此我们往outer外层寻找,全局里是有i的,不过这个时候i已经是10了。

还在觉得闭包难懂?看这里!没问题!

我们改为let,就是我们想要的结果了,因为会形成块级作用域,那么outer不是指向全局,而是函数所处的块级作用域也就是词法作用域。而这个时候i都是当前的值,因此会输出0-9.

闭包实例

了解基本知识后,我们直接上闭包。

还在觉得闭包难懂?看这里!没问题!

我们先给出说明

还在觉得闭包难懂?看这里!没问题!

执行过程

  1. 我们首先创建一个全局执行上下文,给好基本数据
  2. 开始执行,foo函数调用,那么在此之前我们要创建一个foo函数的执行上下文,并放入基本数据。
  3. 返回bar函数给调用者。这个时候foo函数就彻底用完了,我们按理来说应该要回收,可是我们明显后面要调用foo里的函数呀,那这个时候闭包的作用就出来了,开一个小背包,里面存储后面调用时要用到的东西。这里存的是Tom
  4. 按照词法作用域的原理,bar的outer指向foo的闭包,因此最后打印结果为Tom。

在JavaScript中,根据词法作用域的规则,内部函数一定能访问外部函数中的变量,当内部函数被拿到外部函数之外调用时,即使外部函数执行完毕,但是内部函数对外部函数中的变量仍然存在引用,那么这些被引用的变量,会以一个集合的方式保存下来,这个集合就是闭包。 作用: 实现变量的私有化

闭包应用

还在觉得闭包难懂?看这里!没问题!

如果说我们要通过调用一个函数来实现返回递增的值,在此之前我们只能通过定义全局变量去实现,这样子非常不优雅,但现在我们在函数里面定义函数,然后外面调用,通过闭包优雅实现效果。 此题我们给出的过程如下

还在觉得闭包难懂?看这里!没问题!

小结

JavaScript 中的闭包具有以下特点:

  1. 访问外部作用域变量: 内部函数可以访问外部函数的作用域中的变量和参数,即使外部函数已经执行完毕。
  2. 保留外部作用域链: 内部函数在定义时会保留对外部函数作用域链的引用,使得内部函数可以继续访问外部函数的作用域。
  3. 延长变量生命周期: 由于闭包会持有外部函数作用域中的变量,使得这些变量的生命周期得以延长,直到闭包不再被引用。
  4. 创建私有变量和方法: 通过闭包可以创建私有变量和方法,实现封装和信息隐藏,从而防止外部直接访问和修改。
  5. 保存函数状态: 闭包可以保存函数执行时的状态,例如在计数器或迭代器中使用闭包保存状态。
  6. 解决异步操作中的作用域问题: 闭包可以用于解决异步操作中的作用域问题,例如在事件处理程序中访问外部变量。
  7. 函数作为参数传递: 闭包可以将函数作为参数传递给其他函数,实现回调函数等功能。
  8. 内存泄漏风险: 由于闭包会持有外部作用域中的变量引用,如果闭包被长时间引用而不释放,可能会导致内存泄漏问题,需要注意内存管理。
转载自:https://juejin.cn/post/7366567675314601993
评论
请登录