我刚开始阅读Functional JavaScript并立即介绍了一个我不理解的函数:
function splat(fun) {
return function(array) {
return fun.apply(null, array);
};
}
var addArrayElements = splat(function(x, y) { return x + y });
addArrayElements([1, 2]);
//=> 3
splat(function(x, y) { return x + y })
如何运作。它是使用数组[1,2]
调用的,但似乎调用splat
内的匿名函数需要两个参数,而不是一个数组。
将console.log(fun)
放在此代码的第2行上会显示fun
是匿名function(x, y) { return x + y }
的全部内容。 console.log(array)
后return function(array) {
显示该数组为[1, 2]
。 array
来自哪里?
非常感谢。
答案 0 :(得分:2)
addArrayElements = function(array) { fun.apply(null, array); };
BUT
它有一个closure,其中包含范围的变量上下文(创建匿名函数的splat
函数的变量上下文)仍然可见且可访问。
在JavaScript中,函数是第一类对象,可以作为参数引用和传递,或者在本例中通过闭包机制传递。
编辑:关于JavaScript和范围
在大多数语言中,默认情况下,变量是它们定义的范围的本地变量(通常是函数的本地符号表)。相比之下,在JavaScript中,只有使用var
关键字定义变量时,变量才是本地变量。否则,符号将回溯到包含范围的链中,直到隐含的根对象(在Web浏览器的情况下为window
。即。
function foo() { someVar = "bar"; }
foo();
alert(someVar); // shows "bar"
不受限于本地范围,该符号已(有意或无意)泄露到根范围。
更进一步:
function foo() {
var baz = function() {
someVar = "bar";
};
baz();
}
foo();
alert(someVar); // shows "bar"
但是,如果你在foo中声明someVar:
function foo() {
var someVar;
var baz = function() {
someVar = "bar";
};
baz();
alert("someVar in foo=" + someVar); // shows "bar"
}
foo();
alert("someVar in root=" + window.someVar); // shows "undefined"
请注意,在最后一个版本中,我需要使用window.someVar而不仅仅是someVar,因为someVar从未在根作用域中定义为变量,也不是根对象的属性,这会导致错误。
答案 1 :(得分:2)
//Every time we call this function, we get another one back
function splat(fun) {
return function(array) { // <-- this one will be returned in splat();
return fun.apply(null, array);
};
}
//Step one, call splat, pass a function as parameter
var addArrayElements = splat(function(x, y) { return x + y });
/*
Get back a function that accepts an array, and will execute the function we just passed in on it
*/
// This will call the newly created function, func will be available because it's in a closure
addArrayElements([1, 2]);
最后一点是,即使匿名函数有两个参数,我们也会在其上调用apply,以便绑定array[0] ==> x
和array[1] ==> y
答案 2 :(得分:2)
如果不使用.apply
方法,可以更简单地了解如何编写此函数:
function splat(fun) {
return function(array) {
return fun(array[0], array[1]);
};
}
首先你调用splat,传递一个函数:
var add = function(x,y){ return x + 1 };
var ff = splat(add);
此时,ff指的是function(array)
函数,这意味着它是一个单参数函数。私有变量fun
引用add
函数。
现在,你调用ff传递它的一个参数
ff([1,2]);
并使用数组中的值以两个参数
调用fun
return fun(array[0], array[1]);
这个和真实示例之间的唯一区别是apply
方法允许您使用任何参数数组长度,而不是像我一样硬编码特定长度(2)。
答案 3 :(得分:2)
这是高阶函数的示例。这是一个函数,它将函数作为参数并返回函数而不仅仅是常规值(尽管函数 “只是常规值”在Javascript中)。在这种情况下:
function splat(fun) {
splat
将函数作为其参数...
return function(array) {
...并返回一个带有数组的新函数......
return fun.apply(null, array);
...并在调用时调用第一个fun
函数,并将数组.applied
作为参数。
所以splat
接受一个函数,它需要几个参数并将它包装在一个函数中,该函数接受一个参数数组。名称“splat”来自Ruby等语言,其中函数参数列表中的*
(“splat”或“squashed bug”)会将任意数量的参数累积到数组中。
var addArrayElements = splat(function(x, y) { return x + y });
addArrayElements
现在基本上是:
function (array) {
// closed over variable:
// var fun = function(x, y) { return x + y }
return fun.apply(null, array);
}
这是通过闭包实现的,关闭并“保留”原来的fun
传递给新返回的splat
功能
答案 4 :(得分:0)
一个更实用的方法是使用bind(),它足够短,你不再需要splat,并且总是很好的消除闭包:
var addArrayElements = Function.apply.bind( function(x, y) { return x + y } , null );
addArrayElements([1, 2]); // === 3