2

我刚开始阅读函数式 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

非常感谢。

4

5 回答 5

2
addArrayElements = function(array) { fun.apply(null, array); };

它有一个闭包,其包含范围的变量上下文(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 从未被定义为根范围内的变量或根对象的属性,这会导致错误。

于 2013-06-30T05:14:18.720 回答
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] ==> xarray[1] ==> y

于 2013-06-30T05:17:25.560 回答
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)。

于 2013-06-30T05:18:28.577 回答
2

这是一个高阶函数的例子。这是一个将函数作为参数并返回函数而不仅仅是常规值的函数(尽管函数在 Javascript“只是常规值”)。在这种情况下:

function splat(fun) {

splat将函数作为参数...

return function(array) {

...and returns a new function which takes an array...

return fun.apply(null, array);

...and when called calls the first fun function with the array .applied as its arguments.

So splat takes one function which expects several parameters and wraps it in a function which takes an array of parameters instead. The name "splat" comes from languages like Ruby, where a * (a "splat" or "squashed bug") in the parameter list of a function accumulates an arbitrary number of arguments into an array.

var addArrayElements = splat(function(x, y) { return x + y });

addArrayElements is now basically:

function (array) {
    // closed over variable:
    // var fun = function(x, y) { return x + y }
    return fun.apply(null, array);
}

Here this is realized by a closure, which closes over and "preserves" the original fun passed to splat in the new returned function.

于 2013-06-30T05:19:51.243 回答
0

a more functional approach uses bind(), which is short enough you don't really need splat anymore, and it's always nice to eliminate closures:

var addArrayElements = Function.apply.bind( function(x, y) { return x + y } , null );

addArrayElements([1, 2]); // === 3
于 2013-06-30T06:18:57.497 回答