search

Home  >  Q&A  >  body text

javascript - 问道js题目 编写add函数 然后 add(1)(2)(3)(4) 输出10 再考虑拓展性

之前参加过一次笔试
里面有道题目就是编写一个add函数

add(2)(3)(4) //输出9

然后再考虑他的拓展性
当时我就懵逼了

网上也查过相关的解答 但还是看不懂....

这是网上的一个解答

function add(x) {
    var sum = x;
    var tmp = function (y) {
        sum = sum + y;
        return tmp;
    };
    tmp.toString = function () {
        return sum;
    };
    return tmp;
}
console.log(add(1)(2)(3));  //6
console.log(add(1)(2)(3)(4));   //10

疑惑的是这部分

    var tmp = function (y) {
        sum = sum + y;
        return tmp;
    };
    tmp.toString = function () {
        return sum;
    };
    return tmp;

求好心人解答

巴扎黑巴扎黑2902 days ago468

reply all(7)I'll reply

  • 黄舟

    黄舟2017-04-10 16:55:51

    以下回答假设您已经了解闭包的的相关知识

    add(2)(3)(4) //输出9

    可以解析为
    add(2)返回函数A
    A(3)返回函数B
    B(4)返回函数C
    那么初步的做法是
    add函数执行后返回一个函数对象,这个函数对象执行后再返回一个新函数,这样一直执行下去

    function add(num){
        var sum=0;
        sum= sum+num;
        return function(numB){
            sum= sum+ numB;
            return function(numC){
                sum= sum+ numC;
                return sum;
            }
        }
    }

    代码这样实现后,执行

    var result=add(2)(3)(4);

    输出的result为9,符合题目的要求

    然后再考虑他的拓展性

    上面的代码符合我只调用3次函数的情况,
    如果要求计算

    var result=add(2)(3)(4)(5);

    上面的实现代码就歇菜了~~~
    子所以出现问题是上面的实现最后返回一个变量值,而不是一个函数对象。
    好吧,那么我们按前面的写法,继续返回函数,

    function add(num){
        var sum=0;
        sum= sum+num;
        return function(numB){
            sum= sum+ numB;
            return function(numC){
                sum= sum+ numC;
                return function(numD){
                    sum= sum+ numD;
                    return function....
                };
            }
        }
    }

    接下来的问题是如果要调用的次数是未知的(实际上也是未知的),继续按上面代码的写法,是一个不可能完成的任务
    但是我们可以发现返回的每一个函数执行的逻辑都是一样的,就此我们可以精简下代码,让函数返回后返回自身,哈哈这就是链式调用的写法,嗯嗯add(2)(3)(4)就是一个链式调用

    function add(num){
        var sum=0;
        sum= sum+num;
        return function tempFun(numB){
            sum= sum+ numB;
            return tempFun;
        }
    }

    但是

    var result=add(2)(3)(4)(5);
    console.log(result);

    并没有输出我们预料的结果14而是一个函数的字符串表示,想想也不奇怪,你每次函数调用后返回的一个函数对象,那么console.log输出就是一个函数对象的字符串表示了。
    那么怎么能把结果输出呢?
    2种方法
    第1种方法,在函数中添加判断,当没有输入参数时,直接返回调用的结果而不是返回函数

    function add(num){
        var sum=0;
        sum= sum+num;
        return function tempFun(numB){
            if(arguments.length===0){
                return sum;
            }else{
                sum= sum+ numB;
                return tempFun;
            }
    
        }
    }

    调用时和前面的有点区别

    var result=add(2)(3)(4)(5)();
    console.log(result);//输出14

    第2中方法利用JS中对象到原始值的转换规则

    当一个对象转换成原始值时,先查看对象是否有valueOf方法,如果有并且返回值是一个原始值,
    那么直接返回这个值,否则没有valueOf或返回的不是原始值,那么调用toString方法,返回字符串表示

    我们就为函数对象添加一个valueOf方法和toString方法

    function add(num){
        var sum=0;
        sum= sum+num;
        var tempFun=function(numB){
            if(arguments.length===0){
                return sum;
            }else{
                sum= sum+ numB;
                return tempFun;
            }
    
        }
        
        tempFun.valueOf=function(){
            return sum;
        }
        tempFun.toString=function(){
            return sum+'';
        }
        
        return tempFun;
    }
    var result=add(2)(3)(4)(5);
    console.log(+result);//输出14 valueOf
    console.log(result);//输出14 toString

    个人认为这样的写法很不好~函数调用语义不清晰

    PS:还可以这样实现

    var add=(function(){
        var args=[];
        function addInner(){
            if(arguments.length===0){
                return calResult;
            }else{
                Array.prototype.push.apply(args,Array.prototype.splice.call(arguments,0));
                return add;
            }
    
        }
        function calResult(){
            var result=args.reduce(function(previousValue, currentValue){
                return previousValue+currentValue;
            },0);
            args=[];
            return result;
        }
        addInner.valueOf=function(){
            return calResult();
        };
    
        addInner.toString=function(){
            return calResult()+'';
        };
    
        return addInner;
    }());
    
    
    console.log('%d',add(1)(2)(3)(4));

    reply
    0
  • 高洛峰

    高洛峰2017-04-10 16:55:51

    把一批参数分多次传递这个操作叫curry.. 把curry和最后那个函数解耦的写法可以这样写

    var curry = function(final, arity) {
        var curried = function() {
            // this是每次的参数列表
            // 每次slice()保证curry后的函数仍然是无状态的
            var new_args = this.slice(); 
            for(arg_key in arguments) {
                new_args.push(arguments[arg_key]);
            }
            
            if (new_args.length >= arity) {
                return final.apply(null, new_args);
            } else {
                return curried.bind(new_args);
            }
        };
        
        return curried.bind([]);
    };
    
    var sum4 = function(a,b,c,d) { return a+b+c+d; };
    
    var curried = curry(sum4, sum4.length);
    
    console.log(curried(1,2,3)(4));
    // -> 10
    console.log(curried(1)(2,3)(4));
    // -> 10

    reply
    0
  • PHPz

    PHPz2017-04-10 16:55:51

    我个人认为这道题的目的是考察对函数式编程的了解,具体点就是对 curry 的理解。

    我对 @jokester 的答案稍作了些修改,如下:

    function curry(fn) {
        var slice = [].slice;
        var len = fn.length;
    
        return function curried() {
            var args = slice.call(arguments);
            if (args.length >= len) {
                return fn.apply(null, args);
            }
    
            return function () {
                return curried.apply(null, args.concat(slice.call(arguments)));
            };
        };
    }
    
    var add = curry(function (a, b, c, d) {
        return a + b + c + d;
    });
    
    console.log(add(1)(2)(3)(4)); // 10
    console.log(add(1, 2, 3)(4)); // 10
    console.log(add(1)(2, 3)(4)); // 10

    reply
    0
  • 怪我咯

    怪我咯2017-04-10 16:55:51

    我发现还是我的最短,且没用任何中间变量:

    function add(n) {
      var fn = function(m) {
        return add(n + m);
      };
    
      fn.valueOf = function() {
        return n;
      };
    
      fn.toString = function() {
        return '' + n;
      };
    
      return fn;
    }

    reply
    0
  • PHP中文网

    PHP中文网2017-04-10 16:55:51

    首先:

    var tmp = function (y) {
            sum = sum + y;
            return tmp;
        };

    函数返回它自己是为了实现连续调用同一个函数。类似于jQuery里面为了实现链式调用而在方法中返回this。

    其次:

        tmp.toString = function () {
            return sum;
        };

    前面说了,每次调用都返回一个函数,通过toString将需要的结果输出。因为console.log方法会调用参数的toString方法。

    reply
    0
  • 伊谢尔伦

    伊谢尔伦2017-04-10 16:55:51

    主要的思路上面回答都说很清楚了;
    但是代码

    function add(x) {
        var sum = x;
        var tmp = function (y) {
            sum = sum + y;
            return tmp;
        };
        tmp.toString = function () {
            return sum;
        };
        return tmp;
    }
    console.log(add(1)(2)(3));  //6
    console.log(add(1)(2)(3)(4));   //10

    这段代码实际上还是不能满足要求的,题主测试成功也只是因为所在环境的 console.log 会将结果转为 string 输出,为了 return tmp 不至于输出一个function,所以重新定义了函数 tmp 的 toString 方法,使其返回 sum 值。
    比如如果使用以下代码测试会发现问题

    var a = add(1)(2)(3);
    console.log( typeof a ); // function

    reply
    0
  • 巴扎黑

    巴扎黑2017-04-10 16:55:51

    为什么会输出数字? 那是因为控制台的特性。在chrome下,定义一个函数时候,如果显示的给这个函数定义toString属性,回车,会返回toString的值;若没有显示定义,返回undefined。 调用函数,如果定义的函数的返回值不是函数时或返回的函数没有toString方法时,会直接返回函数的返回值;若返回值为函数有toString方法,控制台会检测返回函数的toString属性,若是函数,则调用他,然后返回 function + 其返回值;否则返回function #Function。

    reply
    0
  • Cancelreply