I am reading about closures on MDN and I can't understand something in the following code:
var test = 1;
function makeFunc() {
var name = 'Mozilla';
function displayName() {
alert(name + ' ' + test);
}
return displayName;
}
//Create myFunction - test should still be set to 1 at this point
var myFunc = makeFunc();
test = 99999;
myFunc();
Why is Mozilla 99999
being printed instead of Mozilla 1
when the function was created while test
was assigned 1
? Shouldn't primitives by value types? I also don't think assignment gets hoisted, just declaration, pretty lost.
I am reading about closures on MDN and I can't understand something in the following code:
var test = 1;
function makeFunc() {
var name = 'Mozilla';
function displayName() {
alert(name + ' ' + test);
}
return displayName;
}
//Create myFunction - test should still be set to 1 at this point
var myFunc = makeFunc();
test = 99999;
myFunc();
Why is Mozilla 99999
being printed instead of Mozilla 1
when the function was created while test
was assigned 1
? Shouldn't primitives by value types? I also don't think assignment gets hoisted, just declaration, pretty lost.
4 Answers
Reset to default 4From the article you linked:
The reason is that functions in JavaScript form closures. A closure is the bination of a function and the lexical environment within which that function was declared. This environment consists of any local variables that were in-scope at the time the closure was created.
Here, name
is closed-over: It's a local variable at the time that the function was created. Though it's not a perfect analogy, you can think of it like the displayName
function has an internal pointer to the stack frame containing all the local variables that were around when you defined the function. If you were to pass in name
, you could see it being kept.
var test = 1;
function makeFunc(name) {
function displayName() {
alert(name + ' ' + test);
}
return displayName;
}
//Create myFunction - test should still be set to 1 at this point
var myFunc = makeFunc("one");
var myFunc2 = makeFunc("two");
test = 99999;
myFunc(); // bines closed-over name "one" with lexical scope test 99999
myFunc2(); // bines closed-over name "two" with lexical scope test 99999
Because test
is in lexical scope, not a closed-over local variable, both makeFunc
and makeFunc2
manipulate the same value in global scope, meaning that resetting test
can manipulate the output even after you've created the closures.
A closure does not merely capture a snapshot of test
when it is created, but captures the entire variable itself. This means that whenever the function myFunc
is called, it will print whatever the value of test
is when it was called, not the value of test
when the closure was created.
In other words, inside the closure, test
is still a variable, not a value. That means it can be changed and manipulated however you want, and anything that uses it will see those changes.
So for your code, as soon as you call myFunc()
on the last line, the runtime will evaluate the expression name + ' ' + test
, and since test is now 99999
, that's what you get.
makeFunc
itself returns a function which is called only after the assignment test = 9999;
. The variable test
is accessed from the 'outer' context (by closure).
This is because test
is not updated when makeFunc()
returns displayName
.
Take a look at name
which I removed from the method makeFunc()
scope to the global scope. When makeFunc()
executes, name
will be updated to Mozilla
from within the method scope. makeFunc()
then returns the function displayName()
without touching/updating test
. test
is only updated after the declaration of var myFunc = displayName() // return value of makeFunc()
.
Changing the value of test
after the declaration of myFunc()
will then alter the console.log()
value of test
.
var test = 1;
var name = "IE";
function makeFunc() {
name = 'Mozilla';
console.log("test inside makeFunc(): " + test);
function displayName() {
console.log(name + ' ' + test);
}
return displayName;
}
//Create myFunction - test should still be set to 1 at this point
console.log("test before myFunc declaration: " + test);
console.log("name before myFunc declaration: " + name);
var myFunc = makeFunc();
console.log("test after myFunc declaration: " + test);
console.log("name after myFunc declaration: " + name);
console.log("myFunc() before updating test");
myFunc();
test = 99999;
console.log("myFunc() after updating test");
myFunc();