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

Not understanding function and how it is called from Functional Javascript

本文关键字:函数 Javascript 调用 不理解      更新时间:2023-09-26

我刚开始阅读函数式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从何而来呢?

多谢。

在不使用 .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(。

//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

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

function splat(fun) {

splat将函数作为其参数...

return function(array) {

。并返回一个接受数组的新函数...

return fun.apply(null, array);

。调用时调用第一个fun函数,数组.applied作为其参数。

因此,splat采用一个需要多个参数的函数,并将其包装在一个接受参数数组的函数中。"splat"这个名字来自Ruby等语言,其中函数参数列表中的*("splat"或"被压扁的错误"(将任意数量的参数累积到数组中。

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);
}

在这里,这是通过闭包实现的,闭关闭并"保留"在新返回函数中传递给splat的原始fun

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 中声明一些 Var:

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 从未被定义为根范围内的变量或根对象的属性,这导致了错误。

一种更实用的方法使用bind((,它足够短,你不再需要splat了,消除闭包总是很好的:

var addArrayElements = Function.apply.bind( function(x, y) { return x + y } , null );
addArrayElements([1, 2]); // === 3