拆解"sum=" + add(1)(2)(3)输出6涉及的知识点

393 阅读3分钟

前言

大家好,我是抹茶。

昨天看到一道很有意思的题目,拆解下实现思路和涉及到的底层原理。 题目如下:

console.log("sum=" + add(1)); // sum=1
console.log("sum=" + add(1)(2)); // sum=3
console.log("sum=" + add(1)(2)(3)); // sum=6

拆解实现思路

  • 首先,从整体去看,"sum="是一个字符串,后面是个+号,所以这里会用到字符串的拼接
  • 其次,形如add(1)(2)其内部要用函数柯里化实现,要接收第二个参数,第一次函数调用应该要返回一个函数。
  • 看输出结果,是等于所有参数的和,所以需要有将所有参数累加的逻辑,前置的条件就是收集记录所有的参数。
  • 回到最外层,字符串拼接的时候,add(1)(2)返回的对象,需要调用toString()方法将其转化为字符串。

一步步,编码实现

首先,定义add函数,它需要接收参数,并返回一个函数

function add(...arg){
    const fn = function (){
        //...
    }
    return fn;
}

add(1)(2)表明需要每次调用都返回一个函数,以接收下一次传入的参数

function add(...arg){
    const fn = function (){
        //...
        return fn;//函数的内部也需要返回函数
    }
    return fn;
}

输出的结果是所有参数的累加和,所以每次函数调用,都需要把入参收集起来。

function add(...arg){
    let args = arg;// 收集入参
    const fn = function (...rest){
        args = args.concat(rest);
       
        return fn;//函数的内部也需要返回函数
    }
    return fn;
}

"sum=" +会调用后面的函数返回的对象的toString()方法(涉及JS引擎底层逻辑,当我们尝试把一个对象当作字符串,JS引擎会调用其toString()方法,如果对象没有toString()方法,就会往它的原型链上找),所以我们需要自己实现这个方法,这个方法要实现的功能是把所有的入参相加,并返回。

function add(...arg){
    let args = arg;// 收集入参
    const fn = function (...rest){
        args = args.concat(rest);
        
        return fn;//函数的内部也需要返回函数
    }
    fn.toString = function(){
        const sum = args.reduce((total,cur)=> total + cur,0);
        return sum;
    }
    return fn;
}

我们运行测试一下

image.png

结果是正确的。

下面是扩展思考部分,如果我们没有在fn上增加toString()方法,会如何?

如果对象没有toString方法,就会往它的原型链上找,fn的原型链指向的是Function.prototype,而Function.prototype.toString()默认返回一个表示该函数源码的字符串。

image.png image.png 回到解题的主线,我们的代码是否还有可以优化的地方?

现在我们的toString()方法返回的数据类型是数字,而JS规范中,toString()方法返回的是一个字符串。而且,如果我们没有返回字符串,在与"sum="做字符串拼接的时候,JS引擎内部也会将其转换成字符串再做拼接。

image.png
function add(...arg){
    let args = arg;// 收集入参
    const fn = function (...rest){
        args = args.concat(rest);
       
        return fn;//函数的内部也需要返回函数
    }
    fn.toString = function(){
        const sum = args.reduce((total,cur)=> total + cur,0);
        return sum + ''; //toString()方法按JS底层逻辑应该要返回字符串
    }
    return fn;
}

虽然我们的toString()方法返回字符串底层还是JS引擎的类型转换,但是相比于在"sum="+的时候再进行类型转换,遵循toString()方法返回字符串的会更符合业界的代码风格规范。

总结

本文研究了"sum=" + add(1)(2)(3)如何分析设计逻辑,以及背后涉及到的函数柯里化,对象用作字符串时,JavaScript引擎调用其toString()方法,以及toString()应遵循的代码规范。

完整代码

function add (...arg) {
  let args = arg;// 收集入参
  const fn = function(...rest) {
    args = args.concat(rest);
   
    return fn;//函数的内部也需要返回函数
  }
  fn.toString = function() {
    const sum = args.reduce((total, cur) => total + cur, 0);
   
    return sum +'';
  }
  return fn;
}


console.log("sum=" + add(1)); // sum=1
console.log("sum=" + add(1)(2)); // sum=3
console.log("sum=" + add(1)(2)(3)); // sum=6