一、概念
闭包是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 可以被垃圾收集器回收了
四、涉及的其他概念
作用域和作用域链
词法作用域
执行上下文
内存泄漏
自执行函数
垃圾回收机制