什么是闭包?
根据 MDN 的定义:“闭包是指那些引用了另一个函数作用域中变量的函数。换句话说,闭包可以让你访问一个外部函数作用域的变量。在 JavaScript 中,每当函数被创建时,就会创建闭包。”
例如
const getShowName = () => {
const name = "fatfish" // name 是 getShowName 创建的一个局部变量
return () => {
console.log(name) // 使用父函数中声明的变量
}
}
const showName = getShowName()
showName() // fatfish
作为前端开发工程师,我们在许多场景中都在使用它,并且它真的很强大。
1. 解决循环中的问题
for (var i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i) // i 会打印出什么?
}, 1000 * i)
}
我们如何才能使其打印出 0
、1
、2
?
是的,你可以使用闭包来解决这个问题。它将很快打印出 0、1 和 2。
for (var i = 0; i < 3; i++) {
((n) => {
setTimeout(() => {
console.log(n)
}, 1000 * n)
})(i)
}
当然,我们有另一种更简单的方法,只需要用 let
替换 var
就可以解决这个问题。
for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i)
}, 1000 * i)
}
2. 记忆函数
利用闭包的特性,我们可以减少计算量,提高程序的性能。
const memoize = (callback) => {
const cache = new Map()
return function (n) {
if (cache.has(n)) {
return cache.get(n)
} else {
const res = callback.call(this, n)
cache.set(n, res)
return res
}
}
}
const fibonacci = memoize(function fib(n) {
if (n === 1) {
return 1
}
if (n === 2 || n === 3) {
return 2
}
return fib(n - 1) + fib(n - 2)
})
console.log(fibonacci(1)) // 1
console.log(fibonacci(10)) // 68
console.log(fibonacci(10)) // 68 从缓存中读取,而不是再次计算
3. 封装私有变量和属性
很久以前,我们经常通过闭包实现私有变量的保护。
我们只能通过 getName
和 setName
来获取和设置 _name
的值。
这样,我们可以有效地防止 _name
被恶意修改。
const createName = (name) => {
let _name = name
return {
getName() {
return _name
},
setName(name) {
_name = name
}
}
}
const p = createName('fatfish')
p.getName() // fatfish
p.setName('medium')
p.getName() // medium
4. 函数柯里化
作为前端开发工程师,我确信你已经学过什么是函数柯里化。让我们试着使用闭包来实现它。
const curry = (callback, ...args) => {
return function (...innerArgs) {
innerArgs = args.concat(innerArgs)
if (innerArgs.length >= callback.length) {
return callback.apply(this, innerArgs)
} else {
return curry(callback, innerArgs)
}
}
}
const add = curry((a, b, c) => {
return a + b + c
}, 1)
console.log(add(2, 3)) // 6
完美。