15

所以我是编程新手,我正在尝试通过 Eloquent Javascript 这本书来学习 JS。

到目前为止一切顺利,直到我用以下代码找到一个示例

function makeAddFunction(amount) {
  function add(number) {
    return number + amount;
  }
  return add;
}

var addTwo = makeAddFunction(2);
var addFive = makeAddFunction(5);
show(addTwo(1) + addFive(1));

注意:show 和alert 一样,只是在教程集成的JS 控制台屏幕上显示变量。

作者说这是一个展示词法作用域如何允许合成函数的例子。 本章在这里

我不明白的是,应该是变量的addTwo和如何可以向函数发送参数,更具体地说,函数如何知道变量发送的参数是参数。addFivemakeAddFunctionaddaddnumber

谢谢你们的帮助!

4

6 回答 6

9

在 javascript 中,函数是一等对象,即可以传递、赋值给变量等。变量 addTwo 和 addFive 包含函数。这些函数由“工厂”函数 makeAddFunction 生成。

addTwo 和 addFive 包含的函数带有它们创建时存在的范围。也就是说,例如创建addTwo时,参数“amount”为2。所以addTwo本质上是以下函数:

function addTwo(number) {
   return number + 2;
}

当有人调用 addTwo() 时,它不会将任何内容传递回 makeAddFunction。MakeAddFunction 已经运行并完成。但是,在 makeAddFunction 中创建的范围(其中“数量”等于 2)在 addTwo 函数中仍然存在。

于 2010-11-07T01:12:16.103 回答
8

addTwo并且addFive是变量——但它们是函数变量。看typeof(addTwo)——这是一个函数。就像你这样做:

var addTwo = function(x) { return x + 2; };

与此相同:

function addTwo(x) { return x + 2; }

(编辑:正如Šime 指出的那样,它们并不完全相同。有关两者之间差异的解释,请参见此处。)

一旦你理解了这一点,这个例子就会很有道理。你甚至可以做这样奇怪的事情,声明一个匿名函数并立即调用它:

var seven = function(x) { return x + 2; }(5);

从字面上看,在物理机器代码级别,与以下内容完全相同:对于与此问题相关的所有目的,这等效于:

function addTwo(x) { return x + 2; }
var seven = addTwo(5);

编辑:

也许一个不那么令人困惑的“前传”如下:

function makeTheAddTwoFunction()
{
    return function(x) { return x + 2; }
}

var addTwo = makeTheAddTwoFunction();

这很愚蠢,但用于说明生成函数的函数。当然,那种函数通常会接受参数,这样它每次都可以创建不同的函数,但是你去吧。

于 2010-11-07T01:09:09.757 回答
6

我认为理解该示例的关键是理解函数可以返回其他函数(就像任何其他变量一样)。记录该代码将大大有助于理解该概念。

/** 
 * Creates an adder function
 * @param {number} amount Amount to add
 * @return {function}  Method that adds 'amount' to its argument. 
 * See the documentation of add for its signature
 */
function makeAddFunction(amount) {      
  /**
   * Everytime makeAddFunction is called, a new instance of add  is created
   * (and returned) that holds on to its copy of 'amount' (through the closure)
   * @param {number} number value to add to 'amount'
   * @return {number} 'amount' + 'number'
   */
  return function add(number) {
    return number + amount;
  };
}

// addTwo now is a reference to a function that when called
// adds 2 to whatever is passed in
var addTwo = makeAddFunction(2);
// addFive Adds 5 to its argument
var addFive = makeAddFunction(5);
// addTwo(1) = 3, addFive(1) = 6, therefore, output is 9
show(addTwo(1) + addFive(1));
于 2010-11-07T02:36:48.347 回答
3

回复:我不明白的是 addTwo 和 addFive,它们应该是变量,如何向函数 makeAddFunction 发送参数?

addTwo 和 addFive 是变量。但它们的值不是简单的标量(数字、字符串等)。相反,它们的值是函数。由于它们的值是函数,因此可以调用这些函数。例如,addTwo(1)

回复:更具体地说,函数 add 如何知道变量发送的参数是参数号?

函数 add 正在调用它的第一个参数号。因此,稍后,当您通过变量(例如 addOne)调用该函数时,赋予 addOne 的第一个参数变为数字。

ps 如果你在想,“自己,这很棘手!” 那么你是对的——这就是这个例子的全部目的,展示一些棘手的东西。您使用这种技术的频率可能会有所不同,从从来没有到每隔一段时间。

于 2010-11-07T01:12:28.260 回答
0

思考这样一段代码的最好方法是替换值并在你的脑海中解释

// when this one is invoked
var addTwo = makeAddFunction(2);

// makeAddFunction
// becomes something like
function makeAddFunction(2) {
  function add(number) {
    return number + 2;
  }
  // return a function, that adds
  // 2 to every number it gets
  return add;
}

// therefore this function call
// will add 2 to 1 and return 3
addTwo(1);
于 2010-11-07T09:24:48.967 回答
0

和往常一样,这里是关于 JavaScript Closures 的 Jibbring 注释。它讨论了范围、执行上下文、变量/属性解析等......

虽然 JavaScript 闭包是词法的,但执行上下文(可能包含属性)是绑定的(想想 Python 或 Ruby),而不仅仅是“自由变量”(如 C# 或 Scala 的情况)。这就是为什么只能在新函数作用域中引入新变量的原因。(现代 Mozilla 实现引入let)。

于 2010-11-07T09:29:25.543 回答