最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 从一道坑人的面试题说函数式编程

    正文概述 掘金(十年踪迹)   2021-03-23   589

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

    ['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);
    }
    

    上面的这段代码里我们通过 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;
    }
    
    let add1 = add.bind(null, 1, 2);
    let add2 = add.bindRight(null, 1, 2);
    
    console.log(add1(3)); //123
    console.log(add2(3)); //321
    
    

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


    起源地下载网 » 从一道坑人的面试题说函数式编程

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元