I thought I understood closures but I guess not. Here is a code sample that shows the issue I run into. I would expect the code to print 'text' to the console.
var inner = function() {
console.log(text);
};
var outer = function(cb) {
var text = 'text';
cb();
};
I run the outer function with the inner function as a cb. In this case, I get var text is undefined. I thought that the inner function would still have a reference to the text variable because of js closure.
//Run outer with a cb of inner.
outer(inner);
Tried using an anonymous function as a cb and gives the same error of text being undefined.
//Anonymous
outer(function() {
console.log(text);
});
Any ideas what I'm missing here? I am using closure in other areas of my program without problems, but clearly I don't fully understand what's going on.
5 Answers 5
The problem here is less the result of closures as it is scope... and I prefer to think of JavaScript scope in terms parent/child relationships.
Any scoped entity (e.g. a function) has access to things defined in its inherited "parent" scopes (visibility travels upwards) -- but "parent" scopes cannot see inside of "child" scopes, and children of the same scope cannot see inside one another.
The image above is generic and shamelessly stolen from Google Images, but serves to explain that "A" is the parent scope; "B" and "C" were defined within the parent scope. "D" and "E" are defined within the scope of "B" -- so "C" cannot see "D" or "E".
In your example, the "parent" scope is the context in which these two functions have been defined (presumably window). Anything defined inside the scope (context) of one of the "children" methods can see the things defined in the scope (context) above it -- so the code inside outer() is aware that inner() exists (both were defined in the parent scope), but neither function can see what exists inside the other. (Note: the parent scope (presumably window) also cannot see what was defined inside each of these functions; it only knows the functions exist.)
But because the visibility only travels upwards, the scope inside inner() does not know about text because it was defined inside outer().
Comments
text is undefined - this isn't a closure issue, it's an issue of not passing a parameter to your callback:
var outer = function(cb) {
var text = 'text';
cb(text); //note the parameter pass in here
};
outer(function(textParam) { //and now the callback passed in accepts a param
console.log(textParam) //name this param whatever you want!
});
Comments
There's no (relevant) closure when calling cb inside outer.
If you created inner inside outer you'd be closing over text, and you would see its value when calling inner–but you're not.
The text inside inner is the global text.
1 Comment
Closures allow functions to access variables in the scope in which they are defined, not the scope in which they are called.
Modifying your example, the following would print "changed text".
var text = 'some text';
var inner = function() {
console.log(text);
};
text = 'changed text'
var outer = function(cb) {
cb();
};
outer(inner);
Comments
You should also study the term Lexical Scoping to understand the situation. The scope of a variable/function is defined by its location within the source code.
A closure is created when a function is declared within a second/outer function and then returned from that function (second/outer). In your case, the inner function is not declared inside the outer function.
You could re-arrange you code to form a closure as:
function outer() {
var text = 'text';
function inner(){
console.log(text);
};
return inner;
}
var foo = outer();
foo(); // text