不理解函数以及如何从Functional Javascript调用它

时间:2013-06-30 05:06:57

标签: javascript

我刚开始阅读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来自哪里?

非常感谢。

5 个答案:

答案 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] ==> xarray[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