四個(gè)關(guān)于 JavaScript 中閉包的有用技巧
什么是閉包?
根據(jù) MDN:“閉包是捆綁在一起(封閉)的函數(shù)及其周圍狀態(tài)(詞法環(huán)境)的引用的組合。換句話說(shuō),閉包使您可以從內(nèi)部函數(shù)訪問(wèn)外部函數(shù)的作用域。在 JavaScript 中,每次創(chuàng)建函數(shù)時(shí)都會(huì)創(chuàng)建閉包。”
例如:
const getShowName = () => {
const name = "fatfish" // name is a local variable created by getShowName
return () => {
console.log(name) // use variable declared in the parent function
}
}
const showName = getShowName()
showName() // fatfish
作為前端開(kāi)發(fā)工程師,我們?cè)诤芏鄨?chǎng)景中都會(huì)用到它,它的功能確實(shí)很強(qiáng)大。
1. 解決循環(huán)中的問(wèn)題
for (var i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i) // What will i print out?
}, 1000 * i)
}
我們?cè)鯓硬拍茏屗蛴?、1、2呢?
是的,你可以使用閉包來(lái)解決這個(gè)問(wèn)題。它很快就會(huì)打印出 0、1 和 2。
for (var i = 0; i < 3; i++) {
((n) => {
setTimeout(() => {
console.log(n) // What will i print out?
}, 1000 * n)
})(i)
}
當(dāng)然,我們還有另一種更簡(jiǎn)單的方法,只需將var替換為let即可解決這個(gè)問(wèn)題。
for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i) // What will i print out?
}, 1000 * i)
}
2.記憶功能
利用閉包的特性,我們可以減少計(jì)算量,提高我們編寫(xiě)的程序的性能。
const memoize = (callback) => {
const cache = new Map()
return function (n) {
if (cache.has(n)) { // When it already exists in the cache, the result will be returned directly, we don't need to calculate it again
return cache.get(n)
} else {
const res = callback.call(this, n)
cache.set(n, res) // After the first calculation, the result will be cached
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 Read from cache instead of computing again
3. 封裝私有變量和屬性
很早以前,我們經(jīng)常通過(guò)閉包來(lái)實(shí)現(xiàn)對(duì)私有變量的保護(hù)。
我們只能通過(guò)getName和setName來(lái)獲取和設(shè)置_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.函數(shù)柯里化
作為一名前端開(kāi)發(fā)工程師,我相信你已經(jīng)了解了什么是函數(shù)柯里化。讓我們嘗試使用閉包來(lái)實(shí)現(xiàn)它。
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
太好了,我們做到了,那你還知道閉包的其他用途嗎?