以下代码摘自Jon Resig的书Secrets of JavaScript Ninja,解释如何使用闭包来实现部分功能应用。但是我在理解变量arg
的意图时遇到了问题。为什么需要它以及它如何简化预先填充函数的一些参数的问题?这个partial
函数的可能应用是什么?
Function.prototype.partial = function() {
var fn = this, args = Array.prototype.slice.call(arguments);
return function() {
var arg = 0; // How does this do the currying
for (var i = 0; i < args.length && arg < arguments.length; i++) {
if (args[i] === undefined) {
args[i] = arguments[arg++]; //This line is where the confusion is
}
}
return fn.apply(this, args);
};
};
编辑:我很困惑,因为args
和arguments
在这里必定相同,因为在调用args = Array.prototype.slice.call(arguments);
args
之后是一个真正的数组对象,它包含所有包含的信息在arguments
。因此,如果undefined
中的某些内容为args
,那么我们怎样才能在arguments
内找到某些内容?
答案 0 :(得分:3)
好的,我正试图一块一块地解释它:
Function.prototype.partial = function() {
var fn = this, args = Array.prototype.slice.call(arguments);
return function() {
var arg = 0;
for (var i = 0; i < args.length && arg < arguments.length; i++) {
if (args[i] === undefined) {
args[i] = arguments[arg++]; //This line is where the confusion is
}
}
return fn.apply(this, args);
};
};
第一行:
var fn = this, args = Array.prototype.slice.call(arguments);
这会将this
的值和arguments
的值存储在两个变量中,因为这两个值都会在以下功能块中被覆盖:
return function() {
//inside here arguments will be whatever is passed to this returned function later.
};
for循环:
var arg = 0;
for (var i = 0; i < args.length && arg < arguments.length; i++) {
}
它基本上会遍历传递给partial
函数的所有参数,并在arg >= arguments.length
之前退出。
if (args[i] === undefined) {
args[i] = arguments[arg++]; //This line is where the confusion is
}
因此,如果args
数组的参数未定义,我们将其替换为arguments
数组中的下一个参数。当每个参数被替换时,原始函数将使用merged arguments array调用:
return fn.apply(this, args);
以下是它在实践中的运作方式:
function xy(arg1, arg2) {
console.log(arg1 + " / " + arg2);
}
var p1 = xy.partial("foo", undefined);
p1("bar") //"foo / bar"
var p2 = xy.partial(undefined, "bar");
p2("foo") //"foo / bar"
var p3 = xy.partial("foo");
p3("bar") //"foo / undefined" --> because you have to activly pass "undefined" otherwise the arguments array is too short
最后但并非最不重要的是,此代码如何使用示例p1 = xy.partial("foo", undefined);
详细说明:
//lets call xy.partial("foo", undefined);
Function.prototype.partial = function() {
//fn = xy
//args = ["foo", undefined]
var fn = this, args = Array.prototype.slice.call(arguments);
//return function that is assigned to p1
//lets call p1("bar")
return function() {
//arguments = ["bar"]
var arg = 0;
//for (var i = 0; i < 2 && arg < 1; i++)
for (var i = 0; i < args.length && arg < arguments.length; i++) {
//first iteration:
//args[0] === "foo" -> nothing happend
//second iteration:
//args[1] === undefined -> args[1] = arguments[0] (= "bar"); arg++;
if (args[i] === undefined) {
args[i] = arguments[arg++]; //This line is where the confusion is
}
}
//at this point: args = ["foo", "bar"];
//now just call the function with the merged array
return fn.apply(this, args);
};
};
答案 1 :(得分:1)
部分函数可帮助您预先填充函数执行所需的函数参数。这有助于以多种方式减少开销。
上面的例子有助于以后填充未定义的参数。
arg 是一个私有变量,用于跟踪未定义的参数并按顺序填充未定义的参数。
args[i] = arguments[arg++];
上面填充未定义参数的行在使用partial时未定义。
实施例: -
函数a(b,c){ 的console.log(B,C); }
//用数值1预先填充第二个参数但是第一个参数仍未定义。这将返回一个新功能。
var g = a.partial(undefined, 1);
//现在,当您实际调用该函数并将其传递给参数时,这将填充先前未定义的参数,即“b”。在这种情况下, var arg 将填充先前的 args 数组位置,这些位置未定义,即args [0]未定义。因此 var arg 将按顺序读取传递的参数并为您填充先前未定义的参数。
g("Hi I was undefined before");
如果不希望 args 永久更改,请按以下步骤更新部分实施。
Function.prototype.partial = function() {
var fn = this, args = Array.prototype.slice.call(arguments);
return function() {
var arg = 0, g;//g will hold the clone of args
g = args.slice(0);//clone the args array and use that instead of original args.
for (var i = 0; i < g.length && arg < arguments.length; i++) {
if (g[i] === undefined) {
g[i] = arguments[arg++];
}
}
return fn.apply(this, g);
};
};
function fn(a, b){ }
var fn = f.partial(undefined, 3), x = fn(2), y= fn(4);//Now this will work
答案 2 :(得分:0)
我认为最好用文字解释。至少,我会试一试:
外部函数产生外部arguments
(一次),内部函数产生内部arguments
(一次或多次)。
同时args
是一个“累加器”,其中第一个(外部)调用的参数和所有后续(内部)调用的参数被逐步存储。这一行:
args[i] = arguments[arg++];
只是在 next-to-arrival 的基础上进行累积。
每次内部呼叫fn.apply(this, args)
可能会也可能不会成功执行。 fn将完全知道它会被咖喱。通常,
undefined
(或null
或false
或-1
)