从一道坑人的面试题说函数式编程

53 阅读2分钟

上面这个写法对于这个问题来说是简单的,和函数式编程关系不大。但是,这个问题如果用过程抽象的思路通用化思考,应该是这样的:

['2', '3', '4'].map(parseInt.bindRight(null, 10));

其中 bindRight 和 bind 方法是相反的,bindRight 相当于从右往左 bind:

function add(x, y, z){

return 100*x + 10 * y + z;

}

let add1 = add.bind(null, 1, 2);

let add2 = add.bindRight(null, 1, 2);

add1(3); //123

add2(3); //321

要实现 bindRight,考虑各种情况,稍稍有些复杂,但也并不太麻烦:

Function.prototype.bindRight = function(thisObj, ...values){

let fn = this, len = fn.length - values.length;

return function(...args){

let rest = [], rargs = values.reverse();

if(len > 0){

rest = args.slice(0, len);

}

return fn.apply(thisObj, rest.concat(rargs));

}

}

这样就实现了我们需要的 bindRight,完整的结果如下:

Function.prototype.bindRight = function(thisObj, ...values){

let fn = this, len = fn.length - values.length;

return function(...args){

let rest = [], rargs = values.reverse();

if(len > 0){

rest = args.slice(0, len);

}

return fn.apply(thisObj, rest.concat(rargs));

}

}

console.log(["2","3","4"].map(parseInt));

console.log(["2","3","4"].map(parseInt.bindRight(null, 10)));

function add(x, y, z){

return 100*x + 10 * y + z;

}

let add1 = add.bind(null, 1, 2);

let add2 = add.bindRight(null, 1, 2);

console.log(add1(3)); //123

console.log(add2(3)); //321

bindRight 代码并不复杂,如果 bindRight 的参数个数比函数形参多,那么简单将参数次序 reverse 之后传给原来的函数,否则将函数前面的参数补齐。

除了这样实现 bindRight 之外,还可以有利用更基础的高阶函数操作组合的方式:

Function.prototype.reverseArgs = function(){

let fn = this;

return function(...args){

return fn.apply(this, args.reverse());

}

}

Function.prototype.fixArgsLength = function(len){

let fn = this;

return function(...args){

args.length = len || fn.length;

return fn.apply(this, args);

}

}

Function.prototype.bindRight = function(thisObj, ...values){

return this.reverseArgs().bind(thisObj, ...values).reverseArgs().fixArgsLength(1);

}

上面的这段代码里我们通过 reverseArgs 和 fixArgsLength 以及 bind 来组合出 bindRight。reverseArgs 是将函数参数顺序倒序的高阶函数,fixArgsLength 是将函数参数个数固定的高阶函数,我们可以看到 bindRight 就是先 reverseArgs 再 bind,最后 fixArgsLength(大家可以思考下为什么要 fixArgsLength)。这样就可以了。

不过话说,就上面这个问题直接用 fixArgsLength 也可以——

["2","3","4"].map(parseInt.fixArgsLength(1)); // [2, 3, 4]

最后是完整代码:

Function.prototype.reverseArgs = function(){

let fn = this;

return function(...args){

return fn.apply(this, args.reverse());

}

}

Function.prototype.fixArgsLength = function(len){

let fn = this;

return function(...args){

args.length = len || fn.length;

return fn.apply(this, args);

}

}

Function.prototype.bindRight = function(thisObj, ...values){

return this.reverseArgs().bind(thisObj, ...values).reverseArgs().fixArgsLength(1);

}

var a = parseInt.bind(null, 10);

console.log(["2","3","4"].map(parseInt));

console.log(["2","3","4"].map(parseInt.bindRight(null, 10)));

console.log(["2","3","4"].map(parseInt.fixArgsLength(1)));

function add(x, y, z){

return 100*x + 10 * y + z;