JavaScript-闭包(closure)

187 阅读4分钟

一、概念

闭包是JavaScript中的一个重要概念,它允许从内部函数访问外部函数作用域。

本质上,闭包是将函数内部和函数外部连接起来的桥梁。

在JavaScript高级程序设计(红宝书)中,闭包被描述为:有权访问其他函数作用域中变量的函数。

创建闭包的常见方式:在一个函数内部创建另一个函数

function closure() {
    var count = 0;
    return function() {
        count++;
        console.log(`第${count}次访问`)
    }
}
var fn = closure()
fn() // 第1次访问
fn() // 第2次访问
fn() // 第3次访问

注:JS作用域相关知识 => 点击这里

应用场景简介

节流防抖。函数柯里化。高阶函数。 vue的响应式原理。 react fab hooks的原理,都是闭包

闭包的一个常见用途是创建可以持久化某些变量的函数,它返回一个内部函数,例如定时器、事件监听器或者在某些特定的计算环境中。但需要注意,闭包可能会导致内存泄漏,因为它们可能会增大引用环境导致的对象无法被回收。

二、用途

JS闭包的用途主要包括:实现封装、模拟面向对象、作为缓存、执行自执行函数等

2.1 实现封装

通过闭包的形式,内部的变量可以在函数外部被访问,但这种访问是通过提供的闭包接口进行的,从而实现了对内部变量的保护和访问控制。

例如,通过闭包可以创建一个对象,该对象具有获取(getName)和设置(setName)内部变量(name)的方法,从而在外部通过这些方法间接访问和修改内部变量的值

function peopleFn() {
    let name = '张三'
    return {
        getName: () => {
            return name
        },
        setName: value => {
            name = value
        }
    }
}

const fn = peopleFn()
console.log(fn.getName())  // 张三
fn.setName('李四')
console.log(fn.getName())  // 李四

2.2 模拟面向对象

闭包可以使得不同的对象(实际上是闭包的实例)拥有独立的成员及状态,互不干涉,从而实现了一种基于原型的对象模型。

2.3 作为缓存

闭包可以用于实现缓存机制,通过闭包返回的函数可以记住起词法作用域,即使在包含它的函数执行完毕后,这些变量仍然可以被闭包访问。这使得闭包可以作为缓存存储数据,避免重复计算或重复创建对象,从而提升性能。

2.4 执行自执行函数

闭包可以用来创建自执行函数,这种函数在创建后立即执行,并且其作用域是独立的,不会污染全局作用域。

这对于创建一次性使用的函数或避免变量冲突非常有用。

(function (){
    var secret = '私密消息';
    
    // 可访问secrtet变量
    function fnSecret() {
        console.log(secret);
    }
    
    // 只在自执行函数内部使用
    fnSecret()  // 私密消息
}())

三、闭包的优点和缺点

3.1 优点

(1)允许在一个作用域内访问另一个作用域的变量。

(2)可以用来创建私有变量,因为外部作用域无法访问内部作用域变量,但是内部作用域可以访问这些变量。

(3)可以防止全局变量的污染,因为他们可以在局部作用域内创建独立的环境。

3.2 缺点

闭包的主要缺点是可能会导致内存泄漏。由于闭包会使得函数内部的变量都被保存在内存中,如果闭包被创建但不被销毁,那么变量就会一直存在,这可能会导致内存消耗过大。

解决这个问题的一个方法就是确保不需要闭包的时候手动释放,例如:将内部函数的引用设置为null

手工释放闭包代码示例

function pClosure() {
    let obj = { name: '小明', age: 18 }
    return function closure() {
        // 使用obj
        console.log(obj)
    }
}

// 常见闭包
let myClosure = pClosure()
// 使用闭包
myClosure()

// 手工释放闭包
myClosure = null
// 现在 obj 可以被垃圾收集器回收了

四、涉及的其他概念

作用域和作用域链

词法作用域

执行上下文

内存泄漏

自执行函数

垃圾回收机制