面试题:JS如何理解闭包问题

82 阅读3分钟

JavaScript闭包是一个重要的概念,对于深入了解JavaScript语言的工作原理和开发高级应用程序非常有帮助。本文将对JavaScript闭包进行详细介绍,包括什么是闭包、闭包的作用和如何创建闭包。

什么是闭包?

闭包是一个函数和其相关变量的组合体,其中函数具有访问其创建时所在作用域中的变量的能力,即使该函数在不同的作用域中执行。

简单来说,闭包就是一个函数,它能够访问在其定义时的词法作用域内定义的变量,即使函数在定义之后被传递到其他作用域中执行,它仍然能够访问这些变量。

闭包的作用

闭包有许多用途,以下是一些常见的用途:

  1. 封装变量:闭包可以用于创建私有变量,防止其他代码意外修改变量的值。

  2. 记忆函数状态:闭包可以用于保存函数的状态,使函数能够“记住”以前的参数和结果,从而避免重复计算。

  3. 实现模块化:闭包可以用于实现模块化,使函数和变量只能通过特定的接口访问,从而避免全局命名空间的污染。

如何创建闭包

在JavaScript中创建闭包的最常见方式是将内部函数返回给外部函数,并在外部函数中定义需要访问的变量。

例如,下面的代码中,外部函数返回内部函数,并在内部函数中访问了外部函数的变量x:

function outerFunction() {
  var x = 10;
  function innerFunction() {
    console.log(x);
  }
  return innerFunction;
}

var inner = outerFunction();
inner(); // 输出10

在上面的代码中,我们定义了一个外部函数outerFunction和一个内部函数innerFunction。outerFunction返回innerFunction,并在内部函数中访问了外部函数的变量x。然后,我们将outerFunction的返回值存储在变量inner中,并调用inner函数来输出变量x的值。由于innerFunction是在outerFunction的作用域中创建的,它可以访问outerFunction中的变量x,即使outerFunction已经执行完毕并且其作用域已经被销毁。

闭包的缺点

需要注意的是,在使用闭包时,必须注意内存泄漏问题,因为闭包会一直持有对其创建时所在作用域的引用,导致该作用域中的变量不能被垃圾回收,从而可能导致内存泄漏。因此,在使用闭包时,应该仔细考虑其生命周期,并确保在不需要时正确地释放闭包。

经典面试问题

// 闭包

for (var i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    console.log(i)
  }, i * 1000)
}

// 解决方案一
// 利用let 块级作用域

for (let i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    console.log(i)
  }, i * 1000)
}

// 解决方案二
// 利用立即执行函数

for (var i = 1; i <= 5; i++) {
  (function(j){
      setTimeout(function timer() {
        console.log(j)
      }, i * 1000)
     })(i)
}

// 解决方案三
// 利用setTimeout第三个参数

for (var i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    console.log(i)
  }, i * 1000, i)
}

实现一个累加器
const counter = function(n){
    return n++
}

const num = counter(10)
num() // 10
num() // 11
num() // 12