认识闭包,理解闭包,掌握闭包

发布于:2024-05-08 ⋅ 阅读:(19) ⋅ 点赞:(0)

在 JavaScript 中,闭包是一个经常被提及但也容易令人困惑的概念。理解闭包的概念和运作方式对于成为一个优秀的 JavaScript 开发者至关重要。本文将深入探讨闭包的定义、原理、应用和实践,帮助读者更好地掌握这一重要概念。(没了解作用域和执行上下文的可以先看这篇文章:

什么是闭包?

闭包是指函数和其相关的引用环境的组合。简而言之,闭包是由函数和定义该函数时的词法环境组成的。闭包使函数可以访问其外部作用域中的变量,即使在函数被返回或传递到其他地方时仍然有效。有些人可能不能理解,我举下面一个例子:

function foo() {

  function bar() {
    var age = 18
    console.log(myName);
  }
  var myName = 'Tom'

  return bar
}
var myName = 'Jerry'
var fn = foo()
fn()  // Tom
  1. 首先,全局作用域中声明了一个变量 myName,其值为 'Jerry'
  2. 然后,调用 foo() 函数,foo() 函数内部声明了一个局部函数 bar(),以及一个局部变量 myName,其值为 'Tom'
  3. 接着,foo() 函数返回了内部的函数 bar,并将其赋值给全局变量 fn
  4. 当执行 fn() 时,实际上是在全局作用域下执行 bar() 函数。在 bar() 函数中,尝试访问变量 myName,但在 bar() 函数内部并未声明 myName,因此会沿着作用域链向上查找。
  5. 在作用域链中,首先查找到的是 bar() 函数内部的作用域,但是在这个作用域中并没有声明变量 myName。接着向上查找到了 foo() 函数的作用域,发现了变量 myName,其值为 'Tom'
  6. 因此,console.log(myName) 会输出 Tom

如果仔细思考的话,你就会产生疑问,bar函数已经赋值给了全局变量fn,这个时候,fn所在的作用域为全局作用域,为什么不会在全局作用域里找myName,我们不妨画出执行上下文:

image.png

当预编译全局后,就要执行,执行到调用foo函数时,再预编译foo,再执行foo,执行完foo后,就要对foo执行上下文进行销毁工作,但是bar函数的词法作用域在foo函数里,可能以后可能会用到foo函数里的东西,你又不得不销毁foo函数,因为这是铁律,用完的东西一定要清除,不这么做,内存都会爆掉,所以就搞出个闭包,当销毁foo执行上下文,就产生了闭包,闭包里存着对bar有用的东西,bar的作用域链就会指向闭包,闭包也会代替foo函数的作用域链指向全局作用域。

看完这个解释后,你就会明白闭包是个什么东西,它有着什么样的作用。

如果把var myName = 'Tom'删掉,那么也会产生闭包,这是因为当一个函数在其定义的作用域外部被调用时,闭包就产生了,只不过foo函数里没有对bar函数有用的东西,闭包内就为空,作用域链就会顺着闭包指向的一端即全局作用域里找myName,因此最后输出为Jerry。

闭包的原理

当一个函数在其定义的作用域外部被调用时,闭包就产生了。在 JavaScript 中,函数可以访问其定义时的作用域中的变量。当函数被调用时,它可以访问这些变量,即使在其定义的作用域之外。

闭包的应用

  1. 模块模式: 闭包可以用于模拟私有方法和私有变量。通过将变量和函数封装在闭包中,可以创建一个模块,其中的变量和函数对外部是不可见的。
  2. 函数工厂: 闭包可以用于创建函数工厂,即生成其他函数的函数。通过返回具有不同初始化参数的函数,可以轻松地创建可定制的函数。
  3. 事件处理程序: 在事件处理程序中经常使用闭包。当事件发生时,闭包可以访问其外部作用域中的变量和函数,从而实现对事件的响应。
  4. 异步操作: 闭包在异步编程中也非常有用。它们可以捕获异步操作发生时的上下文,确保回调函数可以访问正确的变量。

闭包的实践技巧

  1. 避免内存泄漏: 由于闭包会保留对外部作用域的引用,因此在不需要时,要确保取消对闭包的引用,以防止内存泄漏。
  2. 谨慎使用: 虽然闭包非常有用,但过度使用闭包可能会导致代码难以理解和维护。要谨慎使用闭包,并在必要时进行重构。
  3. 优化性能: 闭包可能会导致性能问题,特别是在循环中频繁创建闭包时。在性能敏感的代码中,要注意闭包的使用,尽量减少闭包的创建次数。

结论

闭包是 JavaScript 中一个强大且常用的概念,它可以帮助我们编写更加灵活和强大的代码。通过深入理解闭包的原理、应用和实践技巧,我们可以更好地利用闭包,编写出高效、健壮且易于维护的 JavaScript 代码。