# 高阶函数
该函数接收一个函数作为参数,返回另外一个函数。
filterLowerThan10
这个函数接收一个数组作为参数,会挑选出数组中数值小于10
的项,所有符合条件的项会构成一个新数组并返回
const filterLowerThan10 = array => {
let result = []
for(let i=0;i<array.length;i++) {
let currentValue = array[i]
if(currentValue < 10) result.push(currentValue)
}
return result
}
2
3
4
5
6
7
8
这个例子代码不够优雅,是很明显的面向过程编程,都存在遍历逻辑和for
循环,可以利用filter
的函数式思想进行改写。
# 柯里化
柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。
var add = function(x) {
return function(y) {
return x + y;
};
};
var increment = add(1);
var addTen = add(10);
increment(2);
// 3
addTen(2);
// 12
add(1)(2);
// 3
2
3
4
5
6
7
8
9
10
11
12
13
14
15
这里定义了一个
add
函数,它接受一个参数并返回一个新的函数。调用add
之后,返回的函数就通过闭包的方式记住了add
的第一个参数。所以说bind
本身也是闭包的一种使用场景。
那肯定不能连续多次return
函数的,所以可以这么实现柯里化函数:
function curryIt(fn) {
var args = [];
var result = function(arg){
args.push(arg);
if(args.length < fn.length){ // fn.length获取形参个数
return result;
} else {
return fn.apply(this,args);
}
}
return result;
}
var fn = function(a,b,c) {
console.log(a+b+c) // 6
return a+b+c;
}
curryIt(fn)(1)(2)(3)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
另一种版本:
function curry(fn, args) {
var length = fn.length;
var args = args || [];
return function(){
newArgs = args.concat(Array.prototype.slice.call(arguments));
if (newArgs.length < length) {
return curry.call(this,fn,newArgs);
}else{
return fn.apply(this,newArgs);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
侯策一书的柯里化实现版本
add(1)(2) == 3 // true
每次执行add
函数后一定要保证返回一个函数,以供后续继续调用,且返回的这个函数还要返回自身,以支持连续调用。
const add = arg1 => {
const fn = arg2 => {
return fn
}
fn.toString = function() {
}
return fn
}
2
3
4
5
6
7
8
9
为了进行求和操作,需要在
add
函数内部维护一个闭包变量args
,args
是一个数组,用于记录每次调用时传入的参数,并在toString
方法体中对参数进行求和,在fn
方法体中将当前参数添加到数组中args
中。改写toString
只是为了在==
情况下进行类型转换,如果是想要直接输出结果就用上面的方法
const add = arg1 => {
let args = [arg1]
const fn = arg2 => {
args.push(arg2)
return fn
}
fn.toString = function(){
return args.reduce((prev,item) => prev+item, 0)
}
return fn
}
2
3
4
5
6
7
8
9
10
11
如果为了支持add(1)(2,3)(4)
这种参数形式,可以改写为:
const add = (...arg1) => {
let args = [...arg1]
const fn = (...arg2) => {
args = [...args,...arg2]
return fn
}
fn.toString = function() {
return args.reduce((prev,item) => prev+item, 0)
}
return fn
}
function add() {
let argsArr = Array.from(arguments)
let fn = (...args) => {
argsArr = argsArr.concat(args)
return fn
}
fn.toString = () => {
return argsArr.reduce((prev,curr) => {
return prev + curr
},0)
}
return fn
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 通用柯里化
const curry = (fn,length) => {
length = length || fn.length
return function(...args) {
if(args.length < length) {
return cury(fn.bind(this,...args),length - args.length)
} else {
return fn.call(this,...args)
}
}
}
// 不适用bind
const curry = fn => {
return tempFn = (...arg1) => {
if(arg1.length >= fn.length) {
return fn(...arg1)
} else {
return (...arg2) => tempFn(...arg1,...arg2)
}
}
}
// 简化
const curry = fn =>
judge = (...arg1) =>
arg1.length >= fn.length
? fn(...arg1)
: (...arg2) => judge(...arg1,...arg2)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
TIP
实现原理:先用闭包把传入的参数保存起来,当传入参数的数量足够执行函数时,就开始执行函数。步骤:
- 先逐步接收参数,并进行存储,以供后续使用
- 先不进行函数计算,延后执行
- 在符合条件时,根据已存储的参数进行统一计算
# 反柯里化
反柯里化旨在扩大函数的适用性,使本来作为特定对象所拥有的功能函数可以被任意对象所使用
unCurry
方法的参数是一个希望被其他对象所调用的方法,暂且被称为fn
,unCurry
方法执行后会返回一个新的函数,该函数的第一个参数是预期要执行方法的对象,后面的参数是执行这个方法时需要传递的参数。
function unCurry(fn) {
return function() {
var obj = [].shift.call(arguments)
return fn.apply(obj,arguments)
}
}
2
3
4
5
6
# 利用高阶函数进行缓存
函数返回值缓存。实现一个
memorize
函数,对函数返回值进行缓存,以减少函数实际运行的开销。
const memorize = (fn) => {
let cache = {}
return function(...args) {
let cacheKey = args.join('_')
console.log(cacheKey)
if(cacheKey in cache) {
console.log('命中缓存')
return cache[cacheKey]
} else {
return cache[cacheKey] = fn.apply(this || {},args)
}
}
}
function test(name,age) {
console.log(name,age)
}
let mem = memorize(test)
mem(1,2,3)
mem(1,2,3)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 实现compose
compose
的函数作用就是组合函数,将函数串联起来执行。将多个函数组合起来,一个函数的输出结果是另一个函数的输入参数,一旦第一个函数执行,就会像多米诺骨牌一样。
compose
的参数是函数数组,返回的也是一个函数compose
的参数是任意长度的,所有的参数都是函数,执行方向是自右向左的,因此初始函数一定放到参数的最右面compose
执行后返回的函数可以接收参数,这个参数将作为初始函数的参数,所以初始函数的参数是多元的,初始函数的返回结果将作为下一个函数的参数,以此类推。因此除了初始函数之外,其他函数的接收值是一元的。
const greeting = (name) => `hello ${name}`
const toUpper = (str) => str.toUpperCase()
const add = (str2) => `${str2}哈哈哈害`
const fn = compose(toUpper,greeting)
console.log(fn('linjiaheng'))
const newFn = compose(fn,add)
console.log(newFn('linjiaheng前端工程师'))
// 递归
/**
* 这里关键用到了闭包,使用闭包变量存储结果result和函数数组长度,以及遍历索引,并利用递归思想进行结果的累加计算。
* 整体符合正常的面向过程的思维。
*
*/
function compose(...func) {
let len = func.length,
count = len -1,
result = null
return function f1(...args) {
result = func[count].apply(this,args)
if(count <= 0) {
count = len-1
return result
}
count--
return f1.call(null,result)
}
}
// 迭代
function compose(...func) {
let isFirst = true
return function(...args) {
return func.reduceRight((result,fn) => {
if(!isFirst) {
return fn(result)
}
isFirst = false
return fn(...result)
},args)
}
}
// reduce
function reduceFunc (prev, curr) {
return (...args) => {
return curr.call(this, prev.apply(this, args))
}
}
function compose (...args) {
return args.reverse().reduce(reduceFunc, args.shift())
}
// Promise版本
const compose = (...args) => {
let init = args.pop()
return (...arg) =>
args.reverse().reduce((sequence,func) =>
sequence.then(result => func.call(null,result))
, Promise.resolve(init.apply(null,arg)))
}
// lodash版本
var compose = function(funcs) {
var length = funcs.length
var index = length
while(index--) {
if(typeof func[index] !== 'function') {
throw new TypeError('Expected a function')
}
}
return function(...args) {
var index = 0
var result = length ? func.reverse()[index].apply(this,args) : args[0]
while(++index < length) {
result = funcs[index].call(this,result)
}
return result
}
}
// redux版本
function compose(...funcs) {
if(funcs.length === 0) {
return arg => arg
}
if(funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a,b) => (...args) => a(b(...args)))
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92