我有一个可以用任意参数调用的函数f
。当使用 2 个参数调用它时,它会执行一个操作。当用>2 个参数调用它时,它必须自己折叠其他参数。也就是说,当我们调用 f(a,b,c,d)
时,函数应该重新排列为 f(f(f(a,b),c,d)
.我需要尽可能优化它。我提供了 2 种解决方案并对其进行了基准测试:
alphabet = 'abcdefhijklmnopqrstuvwxyz'.split('');
var last_mark;
benchmark = function(msg){
alert(msg.replace('$TIMEDIFF',Date.now()-last_mark));
last_mark=Date.now();
};
fa = function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z){
if (c) return fa(fa(a,b),c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z);
return a+b;
};
fb = function(a,b,rest){
if (rest) return fb.apply(this,[fb(a,b)].concat(Array.prototype.slice.call(arguments,2)));
return a+b;
};
benchmark("Starting benchmark:");
for (var i=0; i<100000; ++i) fa.apply(this,alphabet);
benchmark("Function 1: $TIMEDIFF");
for (var i=0; i<100000; ++i) fb.apply(this,alphabet);
benchmark("Function 2: $TIMEDIFF");
第一个解决方案更快(200ms 与节点 .js 上的 4000ms 相比)。这能进一步优化吗?
ES5 为该任务引入了一个.reduce()
函数。它为前两项调用某个函数,然后再次调用它,传递第一个调用和第三个项的返回值,然后再次调用它,传递该调用和第四个项的返回值,依此类推。
var f = function () {
return toArray( arguments ).reduce(function ( a, b ) {
return a + b;
});
};
现场演示:http://jsfiddle.net/hpAtB/1/
这样的事情将减少数组的创建和操作
fa = function(a,b){
function recursive(a, b, rest) {
if (rest && rest.length)
return recursive(recursive(a,b), rest.pop(), rest);
return a+b;
}
return recursive(a, b, Array.prototype.slice.call(arguments, 2).reverse());
};
或者像这样将函数调用的数量减少一半:
fa = function(a,b) {
function recursive(a, b, rest) {
var result = a + b;
return rest.length ? recursive(result, rest.pop(), rest) : result;
}
return recursive(a, b, Array.prototype.slice.call(arguments, 2).reverse());
};
我假设.reverse()
的成本值得使用.pop()
而不是.shift()
。您可能想尝试两种方式。
当然,您也可以重用该函数:
fa = (function() {
function recursive(a, b, rest) {
var result = a + b;
return rest.length ? recursive(result, rest.pop(), rest) : result;
}
return function(a,b) {
return recursive(a, b, Array.prototype.slice.call(arguments, 2).reverse());
};
})();
最快的方法似乎是用迭代代替递归。
定义高阶函数folder
var folder = function(func) {
return function() {
var args = Array.prototype.slice.call(arguments), len = args.length, result = args[0], i = 1;
for(; i < len; i++){
result = func.call(this, result, args[i]);
}
return result;
};
};
检查此性能测试:http://jsperf.com/varargs-fold
为什么不使用对象作为参数?
var o = {
a:value1,
b:value2,
...
z:value26
};
fa(o);
function fa(obj){
if(obj.c){ // or any other logic
return fa(obj);
}
return o;
}
或全局变量(不带参数)
fa();
function fa(){
if(o.c){ // or any other logic
o.c = o.d + o.e;
fa();
}
}