面试遇到一个题目,实现add函数,要支持add(1,2,3)以及add(1)(2)(3)两种调用方式。
思路
- 两种调用方式的区别可以根据argument长度来判断
- add(1,2,3)的实现可以通过argument参数的累加来实现
- add(1)(2)(3)的实现就难了,面试的时候没想出来,需要理解函数toString方法和闭包。
函数的toString()
toString()继承自Function.prototype.toString(),直接调用会返回当前函数源代码。在Function需要转换为字符串时,会自动调用函数的 toString 方法,像直接console.log函数。
Function.prototype.toString()覆盖了Object.prototype.toString(),后者直接调用会返回 "[object type]",其中 type 是对象的类型。
可以显示设置函数的toString,从而覆盖默认toString的行为。例如:
function any(a) {
return a + 1
}
any.toString = function() { return 2 };
console.log(any)
// ƒ 2
add(1)(2)(3)实现
版本1:
function add(a){
let ret = function(){
return add
}
ret.toString = function() { return a }
return ret
}
这个版本改写了toString,调用add(1)会打印值1,并且返回add函数。但是它不能累加。
版本2:
function add(a){
let ret = function(b){
a = a + b
return ret
}
ret.toString = function() { return a }
return ret
}
add返回ret函数,ret函数内部依然返回ret,这样就实现了add函数的无限调用; 在ret中每次调用都改变a,利用闭包将累加结果保存到a变量。
最终add函数
function add(a){
if(arguments.length > 1){
return [...arguments].reduce((pre,val)=> pre+val, 0)
}else{
let ret = function(b){
a = a + b
return ret
}
ret.toString = function() { return a }
return ret
}
}
console.log(add(1,2,3));
console.log(add(1)(2)(3))
// 6
// f 6
得到正确结果,同时实现了add(1)(2)(3),add(1,2,3)的支持。
参考