概述
这是我读《javascript函数式编程》时,对链式编程的总结与思考,供以后开发时参考,相信对其他人也有用。
chain
在jquery和underscore里面,它们的内建对象$和_的方法通常会返回一个this,然后就实现了链式调用,这种调用非常方便。
惰性链
惰性就是延迟执行的意思,在编程的世界里,惰性XX听起来非常高大上,但是实现起来也许非常简单。我下面的代码就实现了一个惰性链:
function lazyChain(fn) {
var calls = [];
return {
invoke: function(funcStr, ...args){
calls.push(function(target) {
if(target === undefined) return fn[funcStr](...args);
return fn[funcStr](target, ...args);
});
return this;
},
force: function() {
return calls.reduce((accu, curFunc) => {
if(accu === undefined) return curFunc();
return curFunc(accu);
}, undefined);
}
};
}
有以下几点需要说明:
- 使用了es6的...args来取剩余参数。
- 使用了array函数的reduce方法。
- 主要原理是,在使用invoke调用函数时,实际上没有调用,而是把函数储存在calls这个数组里面,等到执行force方法的时候,再一个个地调用函数。
使用方法如下:
函数无参数的情形:
const fn1 = {
hello: function() {
console.log('hello');
},
mark1: function() {
console.log(',');
},
world: function() {
console.log('world');
},
mark2: function() {
console.log('!');
}
};
//此时没有任何输出,因为是惰性的。
const words = lazyChain(fn1).invoke('hello').invoke('mark1').invoke('world').invoke('mark2');
//此时输出:hello , world !
words.force();
函数有参数的情形:
const fn2 = {
add: function(a,b) {
return a+b;
},
minus: function(a,b) {
return a-b;
},
multiply: function(a,b) {
return a*b;
},
divide: function(a,b) {
return a/b;
}
};
lazyChain(fn).invoke('add', 3, 4).invoke('minus', 2).invoke('multiply', 2).invoke('divide', 4).force(); //相当于(3+4-2)*2/4
chain链的改进
无论是juqery还是underscore里面的链式调用,调用的方法必须是jq对象或者underscore对象拥有的方法,那么如果我们想要调用它所没有的方法,应该怎么优化呢?
我们可以类似上面那样,把方法传入一个数组里面,然后最后用force函数执行,代码如下:
function optiChain() {
var calls = [];
return {
invoke: function(func, ...args){
calls.push(function(target) {
if(target === undefined) return func(...args);
return func(target, ...args);
});
return this;
},
force: function() {
return calls.reduce((accu, curFunc) => {
if(accu === undefined) return curFunc();
return curFunc(accu);
}, undefined);
}
};
}
使用方法如下:
函数无参数的情形:
function hello() {
console.log('hello');
}
function mark1() {
console.log(',');
}
function world() {
console.log('world');
}
function mark2() {
console.log('!');
}
//此时输出:hello , world !
optiChain().invoke(hello).invoke(mark1).invoke(world).invoke(mark2).force();
//如果不用optiChain则需要这么写
hello();
mark1();
world();
mark2();
函数有参数的情形:
function add(a, b) {
return a + b;
}
function minus(a, b) {
return a - b;
}
function multiply(a, b) {
return a * b;
}
function divide(a, b) {
return a / b;
}
//相当于(3+4-2)*2/4
optiChain().invoke(add, 3, 4).invoke(minus, 2).invoke(multiply, 2).invoke(divide, 4).force();
//如果不用optiChain则需要这么写
divide(multiply(minus(add(3, 4), 2), 2), 4);
可以看到,使用optiChain避免了难看的多条调用或者难看的嵌套调用,使代码变得非常清晰!!!