1、什么时高阶函数(两个条件符合一个便是高阶函数)?
(1)一个函数返回一个函数(拆分函数)
(2)一个函数的参数是一个函数(回调)
例子:
函数的before(即在一个函数执行之前先执行其他逻辑代码)
Function.prototype.before = function (beforeFn) {
return (...args) => { // 箭头函数中没有this指向 没有arguments 所以会像上级作用域查找
beforeFn();
this(...args); // 展开运算符 say(1,2,3)
}
}
// AOP 切片 装饰 把核心抽离出来 在核心基础上增加功能
const say = (...args) => { // 剩余运算符把所有的参数组成一个数组
console.log('说话', args);
}
const newSay = say.before(() => {
console.log('您好')
})
const newSay1 = say.before(() => {
console.log('天气很好')
})
newSay(1, 2, 3);
newSay1();
// 执行结果
// 您好
// 说话 [ 1, 2, 3 ]
// 天气很好
// 说话 []
代码解析:
(1)要实现执行一个函数之前先执行一段逻辑的话,即对该函数的功能进行扩展,所以一般会使用JavaScript的原型对函数进行扩展
(2)首先使用原型扩展出Function.prototype.before这个方法,接收一个函数作为参数并返回一个箭头函数
(3)在箭头函数的内部先执行我们想要执行的逻辑也就是say.before()传入的一个箭头函数
(4)箭头函数中没有this指向 没有arguments 所以会像上级作用域查找,即这里的this代表的就是say方法,并且newSay和newSay1接收的是Function.prototype.before返回的函数可以往里面传参数
(5)...args的两层含义:
a、剩余运算符把所有的参数组成一个数组
b、展开运算符 say(1,2,3)
2、React事务中用到的简单高阶函数
即对刚才的例子进行升级,函数执行之前执行一个初始化函数,函数执行之后执行关闭函数
例子:
// 事务 开始的时候 做某件事 结束的时候在做某件事
const perform = (anymethod, wrappers) => {
wrappers.forEach(wrap => {
wrap.initilizae();
})
anymethod();
wrappers.forEach(wrap => {
wrap.close();
})
}
perform(() => {
console.log('说话')
}, [{ // warpper
initilizae() {
console.log('您好')
},
close() {
console.log('再见')
}
},
{ // warpper
initilizae() {
console.log('您好1')
},
close() {
console.log('再见2')
}
}
])
// 执行结果
// 您好
// 您好1
// 说话
// 再见
// 再见2
代码解析:
(1)首先perform函数调用时传入一个函数和一个数组这两个参数,数组中包含多个对象,每个对象里包含初始化方法和关闭方法
(2)当执行perform函数内部逻辑时,先遍历传进来的数组,执行该数组中每个对象的初始化方法,再执行传进来的第一个函数的逻辑,最后再遍历数组,执行每个对象中的关闭方法
3、函数柯里化(简单版)
柯里化 我们可以把一个大函数拆分成很多的具体的功能
例子:
// 柯里化 : 就是将一个函数拆分成多个函数
// 判断类型 Object.prototype.toString.call
// 高阶函数中包含 柯里化 可以保留参数 bind
const checkType = type => {
return content => {
return Object.prototype.toString.call(content) === `[object ${type}]`;
};
};
// 闭包
let types = ["Number", "String", "Boolean"];
let utils = {};
types.forEach(type => {
utils["is" + type] = checkType(type);
});
console.log(utils.isString("123"));
console.log(utils.isNumber("456"));
// 输出结果
// true
// false
代码解析:
(1)在高阶函数中实现简单的函数柯里化,Object.prototype.toString.call方法可以用来判断一个内容的在js中的类型
(2)在checkType函数中使用了闭包来共享参数type,checkType也是一个高阶函数,该函数返回一个函数来执行判断类型的逻辑
(3)要实现多个类型的判断时,需要在调用checkType时,声明一个数组存储这些类型,并遍历出对应的类型来进行调用,生成一个新的函数如isString(),并添加进utils数组中
(4)这里重点解析一句代码:
utils["is" + type] = checkType(type);
使用ES6对象数组语法,utils数组中添加types数组对应的类型属性作为键并将checkType函数作为值,形成键值对
输出utils对象数组的结果如下:
// {
// isNumber: [Function],
// isString: [Function],
// isBoolean: [Function]
// }
4、函数柯里化(通用版)
例子:
// 通用的柯里化
const add = (a, b, c, d, e) => {
return a + b + c + d + e;
};
const curring = (fn, arr = []) => {
// 函数的length 为函数参数的个数
let len = fn.length
return (...args) => {
arr = arr.concat(args); // [1] [1,2,3] < 5
if (arr.length < len) {
return curring(fn, arr) // fn add()
}
return fn(...arr)
}
}
let r = curring(add)(1)(2)(3)(4); // [1,2,3,4,5]
console.log(r);
// 输出结果
// [ 1, 2, 3, 4, 5 ]
// 15
代码解析:
首先将add函数当作参数传进curring函数,curring函数返回一个可以接收可变参数的函数,然后将1,2,3,4,5每次传一个进去,数值参数进行拼接后,如果长度小于add函数的参数个数的话则继续递归调用curring函数,直到满足条件执行add函数将所有值相加。
5、函数柯里化(简单版的延伸)
const curring = (fn, arr = []) => {
// 函数的length 为函数参数的个数
let len = fn.length
return (...args) => {
arr = arr.concat(args); // [1] [1,2,3] < 5
if (arr.length < len) {
return curring(fn, arr) // fn add()
}
return fn(...arr)
}
}
const checkType = (type, content) => {
return Object.prototype.toString.call(content) === `[object ${type}]`;
};
let types = ["Number", "String", "Boolean"];
let utils = {};
types.forEach(type => {
utils["is" + type] = curring(checkType)(type); // 先传入一个参数
});
console.log(utils.isString('hello'));
// after 在...之后
// 输出结果
// [ 'String', 'hello' ]
// true
代码解析:
类似上面的做法,只是多了一步将curring函数返回的函数当作键值对中的值传给utils对象数组中的对象
6、实现after...之后执行某函数
例子:
const after = (times, fn) => { // after可以生成新的函数 等待函数执行次数达到我的预期时执行
return ()=>{
if(--times === 0){
fn();
}
}
};
let newAfter = after(3, () => {
console.log("三次后调用");
});
newAfter();
newAfter();
newAfter();
// lodash after
// 输出结果
// 三次后调用
// 并发的问题 发布订阅 观察者模式
代码解析:
因为after是一个高阶函数返回了一个新的函数,这个新的函数中只有当times降到0时才执行传进来的fn函数,fn函数即after调用传入的第二个参数(箭头函数),可用做功能计时器。