闭包的概念及应用场景

915 阅读2分钟

闭包的概念

一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。---摘自于MDN上对闭包的解释,前往MDN-闭包

通俗点说就是:只要一个函数访问了一个不属于当前函数作用域的变量就称为闭包。

下面看一个MDN上简单的例子:

function init() {
    var name = "Mozilla"; // name 是一个被 init 创建的局部变量
    function displayName() { // displayName() 是内部函数,一个闭包
        alert(name); // 使用了父函数中声明的变量
    }
    displayName();
}
init();

init() 创建了一个局部变量 name 和一个名为 displayName() 的函数。displayName() 是定义在 init() 里的内部函数,并且仅在 init() 函数体内可用。请注意,displayName() 没有自己的局部变量。然而,因为它可以访问到外部函数的变量,所以 displayName() 可以使用父函数 init() 中声明的变量 name 。

从例子上可以看出,其实就是一种作用域链,在displayName内部函数中,访问name属性,但在其内部作用域中没有找到name属性,因此往作用域链上找,也就是在init函数里面找到了name属性。

有意义的闭包

根据上面例子改造:

function init() {
  var name = "Mozilla"
  return function displayName() {
    console.log(name)
  }
}
let fn = init()
fn()

此案例中,在init函数中,返回displayName函数,且在displayName函数内引用了外部函数init的中变量name,此时就形成了一个闭包。

使用场景

柯里化函数

柯里化的目的在于避免频繁调用具有相同参数函数的同时,又能够轻松的重用。

// 假设我们有一个求长方形面积的函数
function getArea(width, height) {
    return width * height
}
// 柯里化之前
// 如果我们碰到的长方形的宽老是10
const area1 = getArea(10, 20)
const area2 = getArea(10, 30)
const area3 = getArea(10, 40)

// 我们可以使用闭包柯里化这个计算面积的函数
function getArea(width) {
    return height => {
        return width * height
    }
}

const getTenWidthArea = getArea(10)
// 之后碰到宽度为10的长方形就可以这样计算面积
const area1 = getTenWidthArea(20)

// 而且如果遇到宽度偶尔变化也可以轻松复用
const getTwentyWidthArea = getArea(20)

闭包总结

  • 形成:  函数中嵌套函数,且内部函数引用外部函数的变量
  • 作用:  延长变量生命周期
  • 优点:  希望一个变量长期存在内存中、模块化代码避免全局变量的污染、私有属性
  • 缺点:  无法回收闭包中引用变量,容易造成内存泄漏