# 闭包与高阶函数
闭包是一个内部函数,它是在另一个函数内部的函数。
# 4.2 真实的高阶函数
tap 函数
const tap = value =>
(fn) => (
typeof fn === 'function' && fn(value),
console.log(value)
)
// tap 函数第一次执行返回一个闭包函数,在这个必包函数内部保存了第一次传入的参数
// 调用
tap('fun')(it => console.log('value is ', it))
// value is fun
// fun
// 使用
forEach([1,2,3], a =>
tap(a)(() => console.log(a))
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
unary 函数
首先来看一下数组 map 方法的使用:
[1, 2, 3].map((a, i, ary) => a * a)
// [1, 4, 9]
1
2
2
可以看到 map 会传入三个参数给函数。假设现在我们要实现把字符串数组转为整数数组,我们有一个内置的函数 parseInt,它接受两个参数 parse 和 radix,如果可能,它将把传入的 parse 转为数字。
['1', '2', '3'].map(parseInt)
// [1, NaN, NaN]
1
2
2
从上面的结果看到,并不是我们期望的,原因是因为 parseInt 拿到了 map 传入的第二个参数。需要把 parseInt 函数转换为另一个只接受一个参数的函数。下面来实现 unary 函数。它的任务是接受一个给定的多参数函数,并把它转化为一个直接受一个参数的函数:
const unary = fn => fn.length === 1 ? fn : (arg) => fn(arg)
// 检测传入的 fn 是否有一个长度为 1 的参数列表(fn.length),如果是就什么都不做,如果没有,就返回一个新的函数,它只接受一个参数 arg,并用该参数调用 fn
// 利用 unary 实现上边的问题:
['1', '2', '3'].map(unary(parseInt))
// [1, 2, 3]
// 简单分析,在 unary 函数内,fn 即为 parseInt,它接受两个参数所以 fn.length 为 2,所以 unary(parseInt) 执行后返回的结果为 (arg) => fn(arg),然后被 map 调用
1
2
3
4
5
6
7
2
3
4
5
6
7
once 函数
const once = fn => {
let done = false
return function () {
return done ? undefined : ((done = true), fn.apply(this, arguments))
}
}
// 使用
var doPayment = once(() => {
console.log('Payment is done!')
})
doPayment() // 'Payment is done!'
doPayment() // undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
memoized 函数
首先有一个纯函数 factorial,用来计算给定数字的阶乘。
const factorial = n => {
if (n === 0) {
return 1
}
return n * factorial(n - 1)
}
1
2
3
4
5
6
7
2
3
4
5
6
7
这里我们知道 factorial 函数只依赖它的参数执行,其他的什么都不需要。问题出现了,为什么不能为每一个输入存储结果呢?如果输入已经在对象中出现,为什么不能直接给出计算结果呢?为了计算 3 的阶乘,必须计算 2 的阶乘,为什么不能重用函数中的计算结果呢?这就是接下来的 memoized 函数要做的事情。
const memorized = fn => {
const lookupTable = {}
return (arg) => lookupTable[arg] || (lookupTable[arg] = fn(arg)) // 注意括号不可以省略,表示一个整体,整个 或 操作符返回的是 fn(arg),并且会做赋值操作
}
// 使用
let fastFactorial = memorized(n => {
if (n === 0) {
return 1
}
return n * fastFactorial(n - 1)
})
console.time()
fastFactorial(150)
console.timeEnd()
// 第一次 default: 0.14208984375ms
// 第二次 default: 0.006103515625ms
// 而且之后所有比 150 小的都不需要再次计算
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21