掌握聚合最新动态了解行业最新趋势
API接口,开发服务,免费咨询服务

用一道坑人的面试题来谈谈函数式编程

一道著名的坑人题,可能一些同学见过:

['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(num.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,完整的结果如下:

https://code.h5jun.com/qig/7/edit?html,js,output

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]

最后是完整代码:

https://code.h5jun.com/fasa/6/edit?html,js,output

如有任何问题,欢迎讨论。

本文链接:https://www.h5jun.com/post/parseInt-to-functional.html

-- EOF --

原文来自:十年踪迹

声明:所有来源为“聚合数据”的内容信息,未经本网许可,不得转载!如对内容有异议或投诉,请与我们联系。邮箱:marketing@think-land.com

  • 全球天气预报

    支持全球约2.4万个城市地区天气查询,如:天气实况、逐日天气预报、24小时历史天气等

    支持全球约2.4万个城市地区天气查询,如:天气实况、逐日天气预报、24小时历史天气等

  • 购物小票识别

    支持识别各类商场、超市及药店的购物小票,包括店名、单号、总金额、消费时间、明细商品名称、单价、数量、金额等信息,可用于商品售卖信息统计、购物中心用户积分兑换及企业内部报销等场景

    支持识别各类商场、超市及药店的购物小票,包括店名、单号、总金额、消费时间、明细商品名称、单价、数量、金额等信息,可用于商品售卖信息统计、购物中心用户积分兑换及企业内部报销等场景

  • 涉农贷款地址识别

    涉农贷款地址识别,支持对私和对公两种方式。输入地址的行政区划越完整,识别准确度越高。

    涉农贷款地址识别,支持对私和对公两种方式。输入地址的行政区划越完整,识别准确度越高。

  • 人脸四要素

    根据给定的手机号、姓名、身份证、人像图片核验是否一致

    根据给定的手机号、姓名、身份证、人像图片核验是否一致

  • 个人/企业涉诉查询

    通过企业关键词查询企业涉讼详情,如裁判文书、开庭公告、执行公告、失信公告、案件流程等等。

    通过企业关键词查询企业涉讼详情,如裁判文书、开庭公告、执行公告、失信公告、案件流程等等。

0512-88869195
数 据 驱 动 未 来
Data Drives The Future