作为一名前端搬砖人,提起闭包我就心梗。

496 阅读3分钟

作为一名前端搬砖人,提起闭包我就心梗。

我听得最多的一句话就是,“闭包,只可意会不可言传”。

简单来说,闭包就是指有权访问另一个函数作用域中的变量的函数。最常见的形态就是在一个函数的内部创建另一个函数。

结构

  1. 外部函数的返回结果是一个函数,暂且称为内部函数。

  2. 内部函数中使用了外部函数中的变量(成员)。

现象

  1. 外部函数执行结束后,被内部函数使用的变量(成员)不会被销毁释放。

  2. 生命周期:在整个代码执行结束时才销毁。

  3. 作用域:只能在函数内部访问。

目的

1)每次去执行返回的内部函数,都可以使用该变量(成员)。并且该变量(成员)的值可以变化,也就是说,该变量(成员)可以在函数多次执行过程中保存状态。在其它一些语言中称为静态变量。例:记录函数调用次数。

2)该变量可以被返回的内部函数访问。但是外部其它代码无法访问,防止意外修改,起到了保护作用。

Demo

例1:

// 闭包
function makeFn() {
    let msg = '函数作为返回值'
    return function() {
        console.log(msg)  // 内部函数访问外部函数的 msg
    }
}​

// 外部函数 makeFn 执行完, 应清理所有, 但其 msg 被 fn 使用, 所以 msg 不会销毁
const fn = makeFn()
fn() // 第一,msg 可以在 fn 中反复使用
console.log(msg)  // 第二,msg 在外部这里是访问不到的,它被保护了起来

例2:

// 闭包
function once (fn) {
    let done = false
    return function () {
        if (!done) {   // 使用(引用)了外部函数的变量
            done = true
            fn.apply(this, arguments)
        }
    }
}​

let pay = once(function (money) {
  console.log(`支付: ${money} RMB`)
})​

pay(5)
pay(5)

例3:

/*
  求数字的平方
    pow(5, 2)
    pow(4, 2)
  可以把 2 固定下来
*/
function makePower(power) {
    return function (number) {
        return Math.pow(number, power)
    }
}​

// 返回 求平方 的函数
let power2 = makePower(2)
console.log( power2(4) )  // 4的平方等于16
console.log( power2(5) )  // 5的平方等于25​

// 返回 求立方 的函数
let power3 = makePower(3)
console.log(power3(4))    // 4的立方等于64
console.log(power3(5))    // 5的立方等于125/*
  我的理解:
      power 作为参数,理解为外部函数的局部变量
      内部函数中引用了它,内部函数被返回
      所以,这是闭包的结构
      
      函数参数的灵活性不同, 有的经常变, 有的偶尔变
      可以把多参数的函数, 细粒度一些, 如上面代码
*//*
Call Stack 调用栈
Scope 作用域
  Local  局部作用域
  Global 全局作用域
  通过var定义的变量会在全局
  Script 通过let定义的变量可以在这个位置看到
Closure (makePower)  说明这个闭包引用了 makePower 外部函数中的变量(成员)
  内部函数执行时, 才可以看到闭包相关内容, 即看到它所引用外部函数中的内容
*/

例4:

/*
  不同级别员工的工资
  生成计算工资的函数. 不同级别员工 基本工资不同
     基本工资 base         12000, 15000 
     绩效工资 performance
*/
function makeSalary (base) {
  return function (performance) {
    return base + performance
  }
}​

// 基本工资为 12000 的  工资计算函数
let salaryLevel1 = makeSalary(12000)
// 基本工资为 15000 的  工资计算函数
let salaryLevel2 = makeSalary(1500console.log(salaryLevel1(2000))  // 总工资 14000
console.log(salaryLevel2(3000))  // 总工资 18000

不管你的理解如何,只要你能写出闭包的案例,你就已经一脚踏入了前端的世界。