开发者

JavaScript example question: lexical scoping/closure - Eloquent Javascript

开发者 https://www.devze.com 2023-01-24 01:06 出处:网络
So I\'m new to programming and I\'m trying to learn JS with the book Eloquent Javascript. So far so good, until I reached an example with the following code

So I'm new to programming and I'm trying to learn JS with the book Eloquent Javascript.

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.

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.

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. Those functions are generated by the "factory" function makeAddFunction.

The functions that addTwo and addFive contain carry with them the scope that existed when they were created. That is, when addTwo, for example, was created, the parameter "amount" was 2. So addTwo, in essence, is the following function:

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

When someone calls addTwo(), it's not passing anything back to makeAddFunction. MakeAddFunction has already run and is finished. However, the scope created within makeAddFunction (in which "amount" equals 2) lingers in the addTwo function.


addTwo and addFive are variables -- but they're function variables. Look at typeof(addTwo) -- it's a function. 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.)

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?

addTwo and addFive are variables. 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)

Re: and more specifically, how does the function add knows that the parameter the variables are sending is the parameter number?

The function add is calling its first parameter number. So later, when you call the function via a variable, eg, addOne, the first parameter given to addOne becomes number.

ps If you're thinking to yourself, "Self, this is pretty tricky!" 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. 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). This is the reason why new variables can only be introduced in new function scopes. (Modern Mozilla implementations introduce let).

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号