mustafa
mustafa

Reputation: 303

Returning functions in javascript, understanding scope & closures

I was looking at Mozillas developer site on javascript closure and they had this example of code.

  function makeAdder(x){
    return function (y) {
        console.log(y + " this is y")
        console.log(x + " this is x")
        return x + y;
        }
}
var add10 = makeAdder(10);
console.log(add10(2)); // 12

Now i understand the X attribute being set but what i dont get is how the scope of the y is being affected. I know its a return function but my brain went to mush trying to visualise how you could set a y when there was no ref to it. could someone explain?

Upvotes: 14

Views: 12036

Answers (5)

David Hedlund
David Hedlund

Reputation: 129832

makeAdder returns a function to which you can pass the y parameter. It is set at the time of invocation, as opposed to x which is set at the time of creation of the new function (at the time of the invocation of makeAdder).

For the case of this example, the output is equivalent to having written:

function add10(y) {
    return 10 + y;
}

console.log(add10(2)); // 12

Nothing new is going on here. The sample code is mainly trying to illustrate that a closure is being created for x.

So makeAdder, here, is aptly named: when you pass 10 to it, it gives you a function that will add 10 to everything you pass to that new function.

var add10 = makeAdder(10);
var add20 = makeAdder(20);

console.log(add10(1) + add20(1)); // 32

Surely, for the purpose of adding, it might be easier to just have a function that accepts two parameters and adds them. But this is not a lesson in adding, it is a lesson in closures.

A real world scenario might be something like this:

var buttons = document.getElementsByClassName('myButton');
for(var i = 0; i < buttons.length; i++) {
    buttons[i].onclick = function() {
        alert('You clicked button ' + i);
    };
}

In the above code, i will have iterated through the entire set before any of the buttons are clicked. Therefore, all buttons will alert whatever buttons.length is. Instead, you could do the following:

var makeAlert = function(x) {
    return function() {
        alert('You clicked button ' + x);
    };
};

for(var i = 0; i < buttons.length; i++) {
    buttons[i].onclick = makeAlert(i);
}

The difference here is that i is not being used when the button is clicked (which will be after the entire iteration), but it is used during the iteration, at a time when i will have a different value for each button.

Instead of creating a variable, makeAlert, you will often see this type of code being written as an anonymous function, invoked immediately. The code below is essentially equivalent to the code above:

for(var i = 0; i < buttons.length; i++) {
    buttons[i].onclick = (function(x) {
        return function() {
            alert('You clicked button ' + x);
        };
    })(i);
}

Upvotes: 16

NibblyPig
NibblyPig

Reputation: 52952

What you're asking for is a function that does something for you:

  function giveMeAFunctionThatBeeps(){
    return function () {
         alert('Beep!');
        }
}

var beeper = giveMeAFunctionThatBeeps();

beeper(); // beeps!

The actual giveMeAFunctionThatbeeps is just a factory that gives you a function that does what you want.

In the example they have provided, you're doing the same thing as the beeper but you're also passing in a value:

  function giveMeAFunctionThatBeepsANumber(x){
    return function () {
         alert('Beep ' + x);
        }
}

This returns a beeper (it's a factory remember), but the beeper alerts the value of x.

However, this value is set when you first create the beeper:

var beeper = giveMeAFunctionThatBeeps(5);

beeper(); // beeps 5!

The beeper is stuck beeping the value 5 now, and we can't do anything about it.

The next example is if you want to create a beeper that beeps any number:

  function giveMeAFunctionThatBeepsANumber(){
    return function (x) {
         alert('Beep ' + x);
        }
}

var beeper = giveMeAFunctionThatBeeps();

beeper(6); // beeps 6!
beeper(7); // beeps 7!

As now we're asking the factory to give us a function we can plug a number into.

Then lastly, the original example, is both of the above combined:

  function giveMeAFunctionThatBeepsANumber(x){
    return function (y) {
         alert('Beep ' + (x + y));
        }
}

var beeper = giveMeAFunctionThatBeeps(2);

When we create the beeper, we're passing in 2. Remember as above, we can't change this afterwards! It will always beep 2...

...but because it's a factory (preconfigured with value 2) returning a function that takes a parameter, we can customise it when we run it:

beeper(6); // beeps 8! because x was set when we created it, and y is what we pass in.

Upvotes: 6

nayaab
nayaab

Reputation: 481

Functions can be seen as special objects that contain executable code as well as properties. Every function has a special [scope] property that represents the environment it was in when it was defined. If a function is returned from another function then this reference to the old environment is closed over by the new function in a "closure".

so when you call var add10 = makeAdder(10) what happens is that the returned function's x has the value 10 which is bound to it's scope, and the call console.log(add10(2)) prints 12.

Consider reading this article for visualizing what are closures. A more detailed explanation of closure could be found here.

Upvotes: 2

alessioalex
alessioalex

Reputation: 63693

So add10 = makeAdder(10); is actually returning this function:

function(y) {
  console.log(y + " this is y")
  console.log("10" + " this is x")
  return 10 + y;
}

Then add10(2) is calling that function, replacing y with 2 everywhere:

  console.log("2" + " this is y")
  console.log("10" + " this is x")
  return 10 + 2;

Upvotes: 1

Matt
Matt

Reputation: 75327

The function makeAdder returns a function when it is called. This function that makeAdder returns accepts one parameter; which is called y.

The variable y exists only during the invocation of the function returned by the makeAdder. It is created each time is is called, and is destroyed when the function returns.

The variable x on the other hand is created when makeAdder is called, and persists due to the closure created by the function makeAdder returns. It will be destroyed when no more references to the function returned exists.

Upvotes: 1

Related Questions