【JS】问道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;
求好心人解答
回答
以下回答假设您已经了解闭包的的相关知识
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));
我个人认为这道题的目的是考察对函数式编程的了解,具体点就是对 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
把一批参数分多次传递这个操作叫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
主要的思路上面回答都说很清楚了;
但是代码
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
我发现还是我的最短,且没用任何中间变量:
function add(n) { var fn = function(m) {
return add(n + m);
};
fn.valueOf = function() {
return n;
};
fn.toString = function() {
return '' + n;
};
return fn;
}
首先:
var tmp = function (y) { sum = sum + y;
return tmp;
};
函数返回它自己是为了实现连续调用同一个函数。类似于jQuery里面为了实现链式调用而在方法中返回this。
其次:
tmp.toString = function () { return sum;
};
前面说了,每次调用都返回一个函数,通过toString
将需要的结果输出。因为console.log方法会调用参数的toString方法。
拓展:
add(2, 5); // 7
add (2)(5); // 7
var add = function(x,r) {
if(arguments.length == 1){ return function(y) { return x + y; };
}else{
return x+r;
}
};
console.log(add(2)(5));
console.log(add(2,5));
这题考的是函数柯里化类似的还有偏函数
var currying = function (fn) { let args = [];
return function() {
[].push.apply(args, arguments);
return fn.apply(null, args);
}
}
var add = currying(function() {
let num = Array.from(arguments)
return num.reduce((x, y) => {
console.log('add', num)
return x + y
})
})
var ride = currying(function() {
let num = Array.from(arguments)
return num.reduce((x, y) => {
console.log('ride', num)
return x * y
})
})
console.log(add(1))
console.log(add(1, 2))
console.log(ride(1, 2, 5))
console.log(ride(10))
看看这个是否能满足需求:
function add(p) { var result = p;
return add = function (p1) {
if (p1) {
result += p1;
return add;
} else {
return result;
}
}
}
函数科里化,在浏览器端可以调用 valueOf 进行转换
function sum(num){ let sumNum = 0;
let args = [].slice.call(arguments);
sumNum += args.reduce(function(a,b){
return a+b;
})
let curryFun = function(numB){
let argsF = [].slice.call(arguments);
if(arguments.length === 0){
return sumNum;
}else {
sumNum += argsF.reduce(function(a,b){
return a+b;
})
return curryFun;
}
};
curryFun.valueOf = function(){
return sumNum;
}
curryFun.toString = function(){
return sumNum;
}
return curryFun;
}
let x = 2;
let y = 5;
console.log(sum(1,3));
console.log(sum(1)(3));
console.log(sum(1)(3,4)(5));
为什么会输出数字? 那是因为控制台的特性。在chrome下,定义一个函数时候,如果显示的给这个函数定义toString属性,回车,会返回toString的值;若没有显示定义,返回undefined。 调用函数,如果定义的函数的返回值不是函数时或返回的函数没有toString方法时,会直接返回函数的返回值;若返回值为函数有toString方法,控制台会检测返回函数的toString属性,若是函数,则调用他,然后返回 function + 其返回值;否则返回function #Function。
以上是 【JS】问道js题目 编写add函数 然后 add(1)(2)(3)(4) 输出10 再考虑拓展性 的全部内容, 来源链接: utcz.com/a/82653.html