Reputation: 129
I am currently working on a relatively simple project and discovered something:
var test = (function() {
var internal = 5;
return {
init: function() {
$(document).on('click', function() {
alert(internal);
});
}
};
}());
test.init();
I thought closure and javascript scope (as I understood it) meant that a function can only access its own variables, and those 1 level above it. So Why does this work? When I click on the document I get an alert of "5", I expected to get undefined.
Here is a JSFiddle showing what I'm doing:
http://jsfiddle.net/mcraig_brs/m644L/1/
Upvotes: 0
Views: 101
Reputation: 499
Javascript is statically scoped. when you are writing a function, you will have access to all the variables available to you inside the function as they are available from where you are accessing it.
var a = 10;
function foo() {
// now i have access in a
var b = 20;
// i have access to both a and b
function bar() {
// still have access to both a and b
var c = 30;
// any more nested function will have access to a,b and c
}
}
Upvotes: 0
Reputation: 1074266
I thought closure and javascript scope (as I understood it) meant that a function can only access its own variables, and those 1 level above it.
Nope, it's all levels above it. In fact, that's how global variables work in JavaScript; they're just an example of closures in action.
So Why does this work?
When the JavaScript engine needs to resolve a symbol, it looks first (loosely) in the execution context that the symbol appears in (in this case, the one created by the call to the anonymous function you're passing into on
). If it doesn't find a matching variable there, it looks at the execution context that surrounds that one (in this case, the one created by calling init
). If it doesn't find it there, it looks at the next one out (the one created by calling your outermost anonymous function). And if not there, the next level out, until it reaches the global execution context.
More about closures (on my blog): Closures are not complicated
Note that I kept saying "...created by the call to..." above. This is a critical point: There can be (almost always are) multiple execution contexts created for a given scope as a program runs. Consider:
function foo(name) {
return function() {
alert(name);
};
}
(This is only two levels again, but it applies to as many levels as you like.)
foo
, when called, creates and returns a function that, when called, shows us the name that was passed into foo
when that function was created:
var f1 = foo("one");
var f2 = foo("two");
f1(); // "one"
f2(); // "two"
Calling foo
creates an execution context. The function foo
creates has an enduring reference to the part of that context that contains variables for that call (the spec calls it the "variable binding object"). That binding object still exists after foo
returns, which is why when we call the function foo
creates, it still has access to the relevant name
variable.
It's important to remember that it isn't that closures get a copy of the value of the variable. They get an enduring reference to that variable. Which is why this works:
function foo(a) {
return function() {
alert(++a);
};
}
var f = foo(0);
f(); // 1
f(); // 2
f(); // 3
Upvotes: 4