昨天学完,今天写笔记,嗯…好习惯
1、高阶函数
函数的本质就是回调。
高阶函数具有下面两个特征,包含任意一个就可以被称之为高阶函数。
一个函数的参数是一个函数
一个函数的返回值是一个函数
2、AOP编程
AOP编程就是切片编程:把核心抽离出来,在核心的基础上增加功能。
假设:我们需要一个函数say
const say = ()=>{
console.log('我是say函数');
}
但是我们需要在say
之前做点什么,于是我们就需要这样say.beform()
。
beform
可能任何函数都会调用。所以我们需要把它注册到原型上,然后它接收一个函数,返回一个函数,(高阶函数的特征)
Function.prototype.before = function(beforeFn) {
return () => { // 箭头函数没有this
beforeFn(); // before
this(); // 这个this是当前调用的函数 -- say
}
}
我们调用一下它。
const newSay = say.before(()=>{
console.log('hi');
})
newSay(); // hi 我是say函数
如果我们需要传参,把想说的话传进去。
newSay(1,2,3);
需要传参的完整代码
Function.prototype.before = function(beforeFn) {
return (...arg) => { // 箭头函数没有this
beforeFn(...arg); // before
this(...arg); // 这个this是当前调用的函数 -- say
}
}
const say = (...arg)=>{
console.log(arg)
}
const newSay = say.before((...arg)=>{
console.log(arg);
})
newSay(1,2,3);
解释:
- 在原型上定义一个方法,接收一个函数,返回一个函数。
- 当调用的时候,nweSay就是
before
返回的那个函数,所以传的参数也到了before
里面的函数上 - 执行
newSay
就是执行before
里面的函数,然后就执行了beforeFn
,this()
。 - 由于是箭头函数,没有
this
,所以在函数里面调用this
就是调用say
函数。 - 会打印出两组
[1,2,3]
3、使用AOP实现一个类型判断函数
类型判断使用Object.prototype.toString.call
所以我们需要一个大概这样的函数,假设判断是否为数字,因为是高阶函数,所以要返回一个函数。
const checkType = ()=>{
return (content)=>{
return Object.prototype.toString.call(content) === `[object number]`
}
}
因为函数要通用,所以我们把number
变成变量
const checkType = (type)=>{
return (content)=>{
return Object.prototype.toString.call(content) === `[object ${type}]`
}
}
调用一下
const isNumber = checkType2('Number');
console.log(isNumber(123)); // true
这样就可以了,但是每次都需要传一个类型,用户万一传错了,或者怎样的不太友好,所以我们需要自己把类型定义好,由用户去调用方法,比如数字就调用isNumber
。
先定义一个类型数组,然后去生成方法名。
let types = ['Number', 'String', 'Boolean', 'Array'];
let utils = {};
types.forEach(type => {
utils['is' + type] = checkType(type);
})
这样各种类型的isXXX
方法就生成好了。
然后就可以调用了
console.log(utils.isNumber(123))
console.log(utils.isString('111'))
console.log(utils.isArray([1,2,3]))
完整代码:
const checkType = (type)=>{
return (content)=>{
return Object.prototype.toString.call(content) === `[object ${type}]`
}
}
let types = ['Number', 'String', 'Boolean', 'Array'];
let utils = {};
types.forEach(type => {
utils['is' + type] = checkType(type);
})
console.log(utils.isNumber(123))
console.log(utils.isString('111'))
console.log(utils.isArray([1,2,3]))
4、柯里化
将一个函数拆分成多个小函数来处理参数就是柯里化,柯里化的好处:可以保留参数。
假设我们要实现下面代码的功能,需要把函数柯里化。
nst add = (a, b, c, d, e) => {
return a + b + c + d + e;
};
let r = curring(add)(1)(2)(3)(4,5);
console.log(r)
我们需要把每次的参数存起来,柯里化会用到回调,所以需要有判断条件,我们要判断参数都拆分完了,然后就不继续回调了,就执行add
方法, 我们需要下面的函数
const curring = (fn,arr = [])=>{
// fn 就是add arr 作参数收集
let len = fn.length; // 函数参数的长度就是函数的长度
return (...args)=>{
arr = arr.concat(args);
if(arr.length < len){
return curring(fn, arr);
}
return fn(...arr)
}
}
let r = curring(add)(1)(2)(3)(4,5);
console.log(r)
解释:
- 顶一个函数,接受原函数和参数,
- 由于我们需要一个条件进行判断,所以我们用原函数的长度作为判断(函数的参数的数量就是函数的长度)。
- 定义一个数组,用作参数收集,最后都放到
add
里面去. - 判断收集到的数组的长度是否小于函数的长度,如果小于则继续回调,否则执行
add
,add
即fn
。
5、柯里化类型判断函数
const checkType = (type)=>{
return (content)=>{
return Object.prototype.toString.call(content) === `[object ${type}]`
}
}
const curring = (fn,arr = [])=>{
let len = fn.length;
return (...args)=>{
arr = arr.concat(args);
if(arr.length < len){
return curring(fn, arr);
}
return fn(...arr)
}
}
let types = ['Number', 'String', 'Boolean', 'Array'];
let utils = {};
types.forEach(type => {
utils['is' + type] = curring(checkType)(type);
})
console.log(utils.isNumber(123))
console.log(utils.isString('111'))
console.log(utils.isArray([1,2,3]))
解释:
- 主要解释这一点
utils['is' + type] = curring(checkType)(type);
- 执行
curring
的时候,把checkType
传进去,并且把type
传进去 - 由于只有一个参数,函数长度也是一个,所以直接就执行
checkType
了,并且把type传递给了checkType
。 - 执行
checkType
,把返回的函数赋值给utils['is' + type]
。