Reputation: 303
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
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
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
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
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
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