Fork me on GitHub

JS-高阶函数

高阶函数
高阶函数以及多个高阶函数嵌套

高阶函数:以另一个函数作为参数的函数

例子:封装一个 map 方法

1
2
3
4
5
let arr=[1,2,3];
arr.map((v,k)=>{
console.log(this.sss);//1
return v*2
},{sss:1})

注意,map方法的第二个参数作为第一个函数的this对象,如果未传入,函数的 this 指向按照函数的类型,是否严格模式等情况讨论,
参见:JS-this 指向
实现

1
2
3
4
5
6
7
8
9
10
11
Array.prototype._map=function(fun,context){
if(typeof fun !=='function'){
throw new TypeError(fn+"is not a function");
}
let len=this.length;
let temp=[]
for(let k=0;k<len;k++){
temp.push(fun.call(context,this[k],k,this))
}
return temp
}

代码组合
高阶函数 widthLogin,判断用户状态
登录判断模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const getLogin=function(){
const getLogin=function(){
var a=parseInt(Math.random()*10).toFixed(0);
if(a%2==0){
return {login:false}
}
return{
login:true
}
}
const withLogin=function(basicFn){
const loginInfo=getLogin();
return basicFn.bind(null,loginInfo)
}
}

主页

1
2
3
4
5
6
7
8
9
10
const withLogin=window.withLogin;
const renderIndex=function(loginInfo){
//判断是否登录
if(loginInfo.login){

}else{

}
}
window.renderIndex=withLogin(renderIndex)

其他模块,处理方法相同,都是在外面包一层 withLogin
如果还需要一个高阶函数判断当前系统的运行环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(function(){
const env={
isMobile:false,
isAndroid:false,
isIOS:false,
}
const ua=navigator.userAgent;
env.isMobile='ontouchstart' in document;
env.isAndroid=!!ua.match(/android/);
env.isIOS=!!us.match(/iphone/);

const withEnvironment=function(basicFn){
return basicFn.bind(null,env);
}
window.withEnvironment=withEnvironment;
})()

此时主页函数调用方法

1
window.renderIndex = withLogin(withEnvironment(renderIndex));

compose方法从右至左,将第一个参数renderIndex作为第二个参数withEnvironment的参数,并将运行结果作为第一个参数的参数,并且最后返回一个新的函数,这个函数拥有两个高阶函数的功能。

1
window.renderIndex = compose(withLogin, withEnvironment, renderIndex);

实现方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function compose(...args) {
let last = args.length - 1;
tag = false;
if (typeof args[last] === 'function') {
tag = true
}
if (last > 1) {
let param = args.pop(args[last]);
last--;
let newParam = args[last].call(args[last], param)
args.pop(args[last])

args.push(newParam);
console.log(newParam);
return compose(...args);
} else if (last === 1) {
if (!tag) {
return args[0].bind(null, args[1])
} else {
return args[0].call(null, args[1])
}
}
}

验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var fn1 = function (a) {
return a + 100
}
var fn2 = function (a) {
return a + 10
}
var fn3 = function (a) {
return a + 20
}

var bar = compose(fn1, fn2, fn3, 10);
console.log(bar());

// 输出结果
// 30
// 40
// 140

上一个函数的运算结果作为下一个函数的参数,这种逻辑与reduce相似。
利用reduce实现compose

1
2
3
4
5
6
function compose(...args) {
return args.reduceRight((pre, cur, i) => {
console.log(pre, cur, i)
return cur(pre)
})
}

借助科里化封装得更加灵活

1
2
3
4
window.renderIndex = compose(withLogin, withEnvironment, renderIndex);

// 还可以这样
window.renderIndex = compose(withLogin, withEnvironment)(renderIndex);

利用lodash.js中是的flowRight来实现

1
2
3
4
5
6
7
// ES6 模块化语法,引入flowRight函数
import flowRight from 'lodash/flowRight';

// ...

// ES6模块化语法 对外暴露接口
export default flowRight(withLogin, withEnvironment)(renderIndex);

参考
高阶函数
代码组合

-------------本文结束感谢阅读-------------