我应该这样做一个咖喱函数,还是最好避免函数构造函数的任何方式?我想知道的是,这是否是“糟糕的做法”,以及如何才能进行其他的赛跑。例如,可以使用Function.bind将参数绑定到函数,但它更像是部分应用程序。递归会对性能产生影响吗?
var curry = function(fn) {
var fnString = fn.toString();
var arity = fn.length;
var paramList = fnString.substring(
fnString.indexOf("(") + 1, fnString.indexOf(")")).split(",");
var body = fnString.substring(
fnString.indexOf("return"), fnString.indexOf("}"));
var newBody = paramList.slice(1).reduce(function(p,c) {
return p + "return function(" + c + ") {";
},"") + body + ( new Array(arity).join("}") );
return new Function(paramList[0], newBody);
};
var test = function(a,b,c,d) {
return a * b * c * d;
};
var curriedTest = curry( test );
console.log( curriedTest(1)(2)(3)(4) ); \\-> 24发布于 2015-08-27 19:41:52
这里的问题并不是Function构造函数本身(尽管这不是一个好主意):恐怕会引起麻烦的是代码的其余部分。
您正在丢弃几乎整个函数体,并且只保留return行。只有第一条return行在那里。
如果将test函数改为如下所示:
var test = function(a,b,c,d) {
var product = a * b * c * d;
return product;
};您只会得到一个错误,因为curried函数变成了:
function(a) {
return function(b) {
return function(c) {
return function(d) {
return product; // ReferenceError
}
}
}
}如果有嵌套函数,它也会中断,例如:
var test = function(a,b,c,d) {
function getRandomNumber() {
return Math.random() * 10;
}
return a * b * c * d * getRandomNumber();
};因为您的代码只查看第一个return行,因此咖喱函数最终只能是return Math.random() * 10。
而且在return line之前不能有任何紧凑型大括号,所以即使是这样的东西也会崩溃:
var test = function(a,b,c,d) {
if(a == 1) {
console.log("got: a == 1");
}
return a * b * c * d;
};因为if的}在return之前,所以最终将错误的东西从函数的身体中分割出来。这会导致new Function调用由于语法错误而失败。
类似地,没有return语句的函数将导致问题:
var test = function(a,b,c,d) {
console.log(a * b * c * d);
};代码中的奇怪评论也会让您感到困惑:
var test = function(a,b,c,d /* more args, y'know */) { ...哦,而且您也没有传播上下文,所以在每一层嵌套中,this可能不会引用正确的/预期的对象。
最后,length并不是一种很好的检查兼容性的方法。函数可以是可变的,使用任意数量的参数,无论是否列出(例如,见下文)。
所以,不,这不是个好办法。此外,Function构造函数只是伪装的eval,应该避免。只需使用bind,或如下所示的函数。
通常的方法是将目标函数包装为-原样。类似于:
function curry(func, args) {
var spice = [].slice.call(arguments, 1);
return function () {
var args = [].slice.call(arguments);
return func.apply(this, spice.concat(args));
};
}这使您可以执行如下操作:
var curried = curry(test, 1, 2, 3);
curried(4); // => 24它适用于上面所有的test函数变体。
如果您确实坚持重写代码和递归嵌套,那么我所能提供的最好的方法(这不是一个好的解决方案)就是根本不重写函数的主体。
当前的解决方案基本上创建了嵌套闭包,这意味着函数的主体可以完全保持不变,只有它的参数必须改变。它将成为最内部最嵌套的闭包。
var curry = function(fn) {
var source = fn.toString(),
args = source.match(/\((.*?)\)/)[1].trim().split(/\s*,\s*/),
body = source.replace(/(^.*?\{|\}[^\}]*$)/g, "");
return args.reverse().reduce(function (nested, arg) {
return new Function(arg, "return " + nested.toString())
}, new Function(args.shift(), body));
};它产生的功能如下:
function anonymous(a) {
return function anonymous(b) {
return function anonymous(c) {
return function anonymous(d) {
// original function body
}
}
}
}有效率?不是的。强壮吗?该死的不!我不建议你用它。
但是,对于上面列出的一些例子来说,它会工作得更好。它仍然会被奇怪的评论所窒息,它不做上下文传播,不能处理变化论,它基本上是一个脆弱的大黑客。但至少它可以处理局部变量、嵌套函数、附加的大括号和缺少的return语句。
https://codereview.stackexchange.com/questions/102066
复制相似问题