一、高阶函数
一个接受函数参数的函数为高阶函数。
不管是函数声明还是函数表达式,函数都指向一个变量。
高阶函数的优点:
(1)对函数内部功能进行抽象,可以传入自定义方法,还可以扩展功能。
(2)函数参数可以作为函数执行后的回调函数执行
let test = function(a, b, fn){
return fn(a) + fn(b)
}
function powFn(a){
return Math.pow(a,a)
}
let res = test(1,2,powFn)
不是什么时候都硬要写高阶函数
function test(a, b, fn){
return fn(a, b)
}
这里的高阶函数的功能只是去调用computor, 没有自己的功能
function computor(a, b){
return a + b
}
console.log(test(1,2,computor))
如果一个高阶函数的功能只是调用另一个函数,就违背了函数单一功能的原则。
funtion test (fn){
return doSth(function(data){
return fn(data)
})
}
function doSth(fn){
return fn()
}
function add (a,b){
return a + b
}
let myFn = test(add)
myFn => doSth(function(data){ return add(data)}) => function(data){return add(data)}() => return add(data)
二、函数柯理化
js函数执行时,不关心是否传参或者参数个数类型。当函数需要完整参数才能执行时,柯理化就派上用场了。
curry(add(1,2), 3,4) === curry(add(1), 2, 3, 4),当参数没传够的时候,函数执行后返回一个函数,再接受参数,知道参数接收完成时,将函数汇总进行计算。
特点:
(1)简化代码,经过柯理化后,很多逻辑都封装在柯理化函数中,只有只需要传参。
(2)提高维护性,柯理化函数返回的函数,是在一个函数体类产生,易维护。
(3)功能单一化,一个函数体内可能由多个任务组成,需要将多个单一功能函数组合起来。
function curry(fn){
let args = [].slice.call(arguments, 1);
return function(){
let innerArgs = Array.from(arguments),
combinedArgs = args.concat(innerArgs);
return fn.apply(this, combinedArgs)
}
}
function add(a,b,c,d){
return a + b + c + d
}
let cum1 = curry(add, 1),
cum2 = curry(add, 1,2);
console.log(cum1(2,3,4))
console.log(cum2(3,4))
三、柯理化工具函数封装
核心步骤,在没接受到足够数量参数时,不执行传入的原函数,返回具有参数判断功能、并包含已传参数的新函数;这个函数在执行时会判断累计的参数是否够数,如果不够则继续返回相同功能的新函数。
function curryFunc(fn,length){
//fn第一次是工具函数,第二次是包含了已传参数的函数
let len = length || fn.length;
//len 来保存待接收参数个数,第一次调用时保存柯理化函数的形参个数,后面用来保存待接受参数个数
//单层函数柯理化方法
function singleStepCurry(foo){
let outterArgs = [].slice.call(arguments,1);
return function(){
let innerArgs = [].slice.call(arguments),
combinedArgs = outterArgs.concat(innerArgs);
//最开始没有返回函数执行结果
return foo.apply(this,combinedArgs)
}
//处理以后,返回包含之前参数和原函数的新函数。
}
return function(){
let length = arguments.length,
currentArgs = Array.from(arguments),
formattedArr = [fn].concat(currentArgs);
//判断参数是否已经接受完毕
if(len > length){
console.log('len',len,'length',length)
let fnCurriedWithArgs = singleStepCurry.apply(this, formattedArr);
return curryFunc( fnCurriedWithArgs , len - length)
// len - length为还需接受参数个数
}else{
//之前没有返回函数执行结果
return fn.apply(this, currentArgs)
}
}
}
function add (a,b,c,d,e){
return a + b + c + d + e
}
var computor = curryFunc(add);
console.log(computor(1,2,3,4))
function curry (fn, length){
//每次更新待接收参数
let len = length || fn.length;
//单层柯理化工具函数
function singleStepCurry (fn){
let outterArgs = [...arguments].slice(1); //没有把原函数排除
return function(){
let innerArgs = [...arguments],
accumulatedArgs = outterArgs.concat(innerArgs);
console.log(innerArgs,accumulatedArgs)
return fn.apply(this,accumulatedArgs)
}
}
return function(){
let currentArgs = Array.prototype.slice.call(arguments),
currentLength = currentArgs.length;
console.log(currentLength)
//判断参数是否够数如果参数没够,再执行一次单层柯理化
if(len > currentLength){
let formattedArr = [fn].concat(currentArgs),
curriedFunc = singleStepCurry.apply(this, formattedArr); //之前没有使用apply传了个数组
return curry(curriedFunc, len - currentLength)
}else{
//fn是每次记录之前参数的函数,里面也包含了原函数。
console.dir(fn)
return fn.apply(this, currentArgs)
}
}
}
function add (a,b,c,d,e){
return a + b + c + d + e
}
let addingComputor = curry(add);
console.log(addingComputor(1)(2)(3)(4)(5))
最终执行的函数里面嵌套这许多之前返回的匿名函数
function(){
// outterArgs => [3]
let innerArgs = [...arguments], // innerArgs => [4]
accumulatedArgs = outterArgs.concat(innerArgs); // accumulatedArgs=> [3,4]
return (function(){
let innerArgs = [...arguments],//[3,4]
accumulatedArgs = outterArgs.concat(innerArgs); //outterArgs => [2]
return (function(){
let innerArgs = [...arguments],
accumulatedArgs = outterArgs.concat(innerArgs); //outterArgs => [1]
return fn.apply(this,accumulatedArgs) // accumulatedArgs => [1,2,3,4]
}).
apply(this,accumulatedArgs) // accumulatedArgs => [2,3,4]
})
.apply(this,accumulatedArgs) // accumulatedArgs => [3,4]
}
如下图: