我是JavaScript的新手,正在尝试理解这篇来自Oreilly JavaScript Cookbook的关于currying的教程。
有没有人可以用通俗易懂的语言一步一步地详细解释一下这个程序。请确保解释在程序的倒数第二行中传递的"null“参数。如果你能帮上忙,我要提前感谢你。
function curry(fn, scope) {
scope = scope || window;
var args = [];
for (var i = 2, len = arguments.length; i < len; ++i) {
args.push(arguments[i]);
}
return function() {
var args2 = [];
for (var i = 0; i < arguments.length; i++) {
args2.push(arguments[i]);
}
var argstotal = args.concat(args2);
return fn.apply(scope, argstotal);
};
}
function diffPoint(x1, y1, x2, y2) {
return [Math.abs(x2 - x1), Math.abs(y2 - y1)];
}
var diffOrigin = curry(diffPoint, null, 3.0, 4.0);
var newPt = diffOrigin(6.42, 8.0); //produces array with 3发布于 2011-03-03 11:46:41
// define the curry() function
function curry(fn, scope) {
// set the scope to window (the default global object) if no scope was passed in.
scope = scope || window;
// Convert arguments into a plain array, because it is sadly not one.
// args will have all extra arguments in it, not including the first 2 (fn, scope)
// The loop skips fn and scope by starting at the index 2 with i = 2
var args = [];
for (var i = 2, len = arguments.length; i < len; ++i) {
args.push(arguments[i]);
}
// Create the new function to return
return function() {
// Convert any arguments passed to the this function into an array.
// This time we want them all
var args2 = [];
for (var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
// Here we combine any args originally passed to curry, with the args
// passed directly to this function.
// curry(fn, scope, a, b)(c, d)
// would set argstotal = [a, b, c, d]
var argstotal = args.concat(args2);
// execute the original function being curried in the context of "scope"
// but with our combined array of arguments
return fn.apply(scope, argstotal);
};
}
// Create a function to be curried
function diffPoint(x1, y1, x2, y2) {
return [Math.abs(x2 - x1), Math.abs(y2 - y1)];
}
// Create a curried version of the diffPoint() function
// arg1: the function to curry
// arg2: the scope (passing a falsy value causes the curry function to use window instead)
// arg3: first argument of diffPoint() to bake in (x1)
// arg4: second argument of diffPoint() to bake in (y1)
var diffOrigin = curry(diffPoint, null, 3.0, 4.0);
// Call the curried function
// Since the first 2 args where already filled in with the curry, we supply x2 and y2 only
var newPt = diffOrigin(6.42, 8.0);在本例中,根本没有使用scope参数。scope设置this对象是什么。你所使用的函数没有使用this,所以它没有实际的效果。作用域是在调用fn.apply(scope, args)时设置的,它既设置要在其中运行的作用域,又提供要传入的参数。
发布于 2011-03-03 11:51:15
如果你不介意一个建议,从Javascript:好的部分开始。接下来是Javascript Pattern,或者是Javascript Ninja的秘密,以获得更高级的技术。食谱更多的是为问题提供固定的解决方案,而不是学习资源。
Matt Ball很好地解释了发生了什么。如果你是初学者,无论如何我都不会费力去弄清楚curry的功能。抛开这一点,我认为这个curry函数很糟糕。这就是我将如何改变它
// this is doing binding and partial function application,
// so I thought bind was a more appropriate name
// The goal is that when you execute the returned wrapped version of fn, its this will be scope
function bind(fn, scope) {
// arguments is an implicit variable in every function that contains a full list
// of what was passed in. It is important to note that javascript doesn't enforce arity.
// since arguments is not a true array, we need to make it one.
// a handy trick for this is to use the slice function from array,
// since it will take arguments, and return a real array.
// we are storing it in a variable, because we will need to use it again.
var slice = Array.prototype.slice,
// use slice to get an array of all additional arguments after the first two
// that have been passed to this function.
args = slice.call(arguments, 2);
// we are returning a function mostly as a way to delay the execution.
// as an aside, that this is possible in a mainstream language is a minor miracle
// and a big part of why i love javascript.
return function() {
// since functions are objects in javascript, they can actually have methods.
// this is one of the built in ones, that lets you execute a function in a different
// context, meaning that the this variable inside the
// function will actually refer to the first argument we pass in.
// the second argument we are jamming together the arguments from the first function
// with the arguments passed in to this wrapper function, and passing it on to fn.
// this lets us partially apply some arguments to fn when we call bind.
return fn.apply(scope, args.concat(slice.call(arguments)));
}
}JavaScript虽然很棒,但非常冗长。在定义绑定时不必要地重复var只会增加很多噪音。而且,没有必要像那样痛苦地构建一个实数组,slice会接受参数并返回一个实数组。特别是在这种情况下,我们使用它两次,我们实际上想切开前两个参数。最后,当您应用并且第一个arg为空时,JavaScript将为您应用全局对象。没有必要显式地这样做。
我的5行函数体去掉了O‘’reillys的11行代码,而且更具可读性。
发布于 2011-03-03 11:54:14
Squeegy发布了一个很好的分解,但我想我也应该添加我的。
//Things to note, 'arguments' is a special variable in javascript that holds
//an array like object full of all the things passed into a function.
//You can test this out with a function like this:
//var alertArgs = function(){alert(arguments);};
function curry(fn, scope) {
//Either use the passed in 'scope' object, or the window object as your scope
scope = scope || window;
//Create a new array for storing the arguments passed into this function
var args = [];
//Loop through the extra arguments (we start at '2' because the first two
//arguments were stored in `fn` and `scope` respectively.
//We store these in the temporary 'args' array.
//(in the example, args will end up looking like: [3.0, 4.0])
for (var i = 2, len = arguments.length; i < len; ++i) {
args.push(arguments[i]);
}
//We return the 'curried' function
return function() {
//This array is not used. I assume it is an error.
var args2 = [];
//We now have a new set of arguments, passed in to the curried function
//We loop through these new arguments, (in the example, 6.42 and 8.0)
//and add them to the arguments we have already saved. In the end, we have
//the args array looking like: [3.0, 4.0, 6.42, 8.0]
for (var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
//This line isn't needed, because args2 is always blank.
var argstotal = args.concat(args2);
//Finally we call the function, passing in the full array of arguments
return fn.apply(scope, argstotal);
};
}
//This function takes 4 arguments
function diffPoint(x1, y1, x2, y2) {
return [Math.abs(x2 - x1), Math.abs(y2 - y1)];
}
//We partially apply the first 2 arguments, so x1 is always 3.0,
//and y1 is always 4.0
var diffOrigin = curry(diffPoint, null, 3.0, 4.0);
//We can now call 'diffPoint' indirectly, without having to specify
//3.0, 4.0 as the first 2 arguments.
var newPt = diffOrigin(6.42, 8.0); //produces array with 3https://stackoverflow.com/questions/5176313
复制相似问题