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

[‘2’, ‘3’, ‘4’].map(parseInt);

说出上面代码的执行结果。

如果不过脑子的说是 [2, 3, 4],那么肯定是错了。实际上,真正的执行结果是 [2, NaN, NaN]。为什么会这样呢?是因为 map 的算子是有两个参数的,第一个参数是被迭代数组的元素,第二个参数是该元素的下标。所以 [‘2’, ‘3’, ‘4’].map(parseInt) 实际上相当于执行了 [parseInt(‘2’, 0), parseInt(‘3’, 1), parseInt(‘4’, 2)],结果就变成了 [2, NaN, NaN] 了。

今天我们在这里主要不是讨论为什么 parseInt(‘2’, 0) 是 2,而 parseInt(‘3’, 1) 是 NaN,实际上我们期望的结果应该是 parseInt(‘2’, 10) 和 parseInt(‘3’, 10),把字符串 ‘2’、‘3’ 转成十进制 number。

所以,正确的写法应该是:

[‘2’, ‘3’, ‘4’].map(num => parseInt(num, 10));

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

[‘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);

}

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!**

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值