简体   繁体   English

JavaScript示例问题:词法范围/闭包 - Eloquent Javascript

[英]JavaScript example question: lexical scoping/closure - Eloquent Javascript

So I'm new to programming and I'm trying to learn JS with the book Eloquent Javascript. 所以我是编程的新手,我正在尝试用Eloquent Javascript这本书来学习JS。

So far so good, until I reached an example with the following code 到目前为止一直很好,直到我用以下代码到达一个例子

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

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

note: show is like alert, only it shows the variables on the screen of a JS console the tutorial has integrated. 注意:show就像alert,只显示教程集成的JS控制台屏幕上的变量。

The author says this is an example to show how lexical scoping allows to synthesise functions. 作者说这是一个展示词汇范围如何允许合成函数的例子。 Chapter here 这里的章节

What I don't understand is how addTwo and addFive , which supposedly are variables, can send parameters to the functions makeAddFunction and add , and more specifically, how does the function add knows that the parameter the variables are sending is the parameter number . 我不明白的是addTwoaddFive (可能是变量)如何将参数发送到函数makeAddFunctionadd ,更具体地说,函数add如何知道变量发送的参数是参数number

Thanks for your help guys! 谢谢你的帮助!

In javascript, a function is a first-class object, that is, it can be passed around, assigned to a variable, etc. The variables addTwo and addFive contain functions. 在javascript中,函数是第一类对象,也就是说,它可以传递,赋值给变量等。变量addTwo和addFive包含函数。 Those functions are generated by the "factory" function makeAddFunction. 这些函数由“factory”函数makeAddFunction生成。

The functions that addTwo and addFive contain carry with them the scope that existed when they were created. addTwo和addFive包含的函数包含它们创建时存在的范围。 That is, when addTwo, for example, was created, the parameter "amount" was 2. So addTwo, in essence, is the following function: 也就是说,例如,当创建addTwo时,参数“amount”为2.所以addTwo实质上是以下函数:

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

When someone calls addTwo(), it's not passing anything back to makeAddFunction. 当有人调用addTwo()时,它不会将任何内容传递给makeAddFunction。 MakeAddFunction has already run and is finished. MakeAddFunction已经运行并完成。 However, the scope created within makeAddFunction (in which "amount" equals 2) lingers in the addTwo function. 但是,makeAddFunction(其中“amount”等于2)中创建的作用域在addTwo函数中存在。

addTwo and addFive are variables -- but they're function variables. addTwoaddFive是变量 - 但它们是函数变量。 Look at typeof(addTwo) -- it's a function. 看看typeof(addTwo) - 它是一个函数。 It's like if you did this: 这就像你这样做:

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

It's the same as this: 它与此相同:

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

(Edit: As Šime pointed out, they aren't exactly the same. See here for an explanation of the difference between the two.) (编辑:正如Šime所指出的那样,它们并不完全相同。请看这里解释两者之间的区别。)

The example will hopefully make sense once you understand that. 一旦你理解了这个例子,希望这个例子有意义。 You can even do weird things like this, declaring an anonymous function and invoking it right away: 你甚至可以做这样奇怪的事情,声明一个匿名函数并立即调用它:

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

Which is literally, at the physical machine code level, the exact same as: Which is equivalent to for all purposes relevant to this question: 从字面上看,在物理机器代码级别,完全相同: 这相当于与此问题相关的所有目的:

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

Edit: 编辑:

Perhaps a less confusing "prequel" to this is the following: 或许对此不那么令人困惑的“前传”如下:

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

var addTwo = makeTheAddTwoFunction();

This is silly, but serves to illustrate functions that make functions. 这很愚蠢,但用于说明函数的功能。 Of course, that sort of function usually will accept arguments so that it can make different functions each time, but there you go. 当然,这种函数通常会接受参数,这样它每次都可以创建不同的函数,但是你去了。

I think the key to understanding that example is understanding that functions can return other functions (just like any other variable). 我认为理解这个例子的关键是理解函数可以返回其他函数(就像任何其他变量一样)。 Documenting that code will go a long way in helping understand that concept. 记录该代码将有助于理解该概念。

/** 
 * 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));

Re: What I don't understand is how addTwo and addFive, which supposedly are variables, can send parameters to the functions makeAddFunction? Re:我不明白的是addTwo和addFive是多少,它们应该是函数makeAddFunction?

addTwo and addFive are variables. addTwo和addFive是变量。 But their values are not simple scalars (numbers, strings, etc). 但它们的值不是简单的标量(数字,字符串等)。 Rather, their values are functions. 相反,他们的价值观是功能。 Since their values are functions, it is fine to invoke those functions. 由于它们的值是函数,因此可以调用这些函数。 Eg, addTwo(1) 例如, addTwo(1)

Re: and more specifically, how does the function add knows that the parameter the variables are sending is the parameter number? Re:更具体地说,函数add如何知道变量发送的参数是参数号?

The function add is calling its first parameter number. 函数add调用它的第一个参数号。 So later, when you call the function via a variable, eg, addOne, the first parameter given to addOne becomes number. 所以稍后,当您通过变量(例如addOne)调用函数时,addOne的第一个参数将变为number。

ps If you're thinking to yourself, "Self, this is pretty tricky!" ps如果你在想自己,“自我,这非常棘手!” Then you're right--and that's the entire purpose of the example, to show something tricky. 那么你是对的 - 这就是这个例子的全部目的,展示一些棘手的东西。 How often you'll use this technique may vary from never to every so often. 你经常使用这种技术的频率可能从不到每一次都有所不同。

The best way to think of a piece of code like this is to substitute values and interpet in your mind 想到这样一段代码的最好方法是在你的脑海里替换价值观和插图

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

As always, here are the Jibbring notes on JavaScript Closures . 与往常一样,这里是关于JavaScript闭包Jibbring笔记 It discusses scopes, execution contexts, variable/property resolution, etc... 它讨论了范围,执行上下文,变量/属性解析等...

While JavaScript closures are lexical, execution contexts (which may contain properties) are bound (think Python or Ruby) and not just "free variables" (as is the case with C# or Scala). 虽然JavaScript闭包是词法,但执行上下文 (可能包含属性)受到约束(比如Python或Ruby),而不仅仅是“自由变量”(就像C#或Scala一样)。 This is the reason why new variables can only be introduced in new function scopes. 这就是为什么只能在新的函数范围中引入变量的原因。 (Modern Mozilla implementations introduce let ). (现代Mozilla实现介绍let )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM