I can't seem to get my head around a specific case of scoping for JavaScript variables. Different from other examples and questions I have found, I am interested in the scoping for nested functions.
I've set up an example at this JSFiddle. The relevant part is the following:
function MyObject() {
var self = this;
var a = 1;
this.b = 2;
var innerMethod = function() {
//1 and 2: direct reference
logMessage("a = " + a); // a = 1
//logMessage("b = " + b); // Error: b is not defined
//3 and 4: using this
logMessage("this.a = " + this.a); // this.a = undefined
logMessage("this.b = " + this.b); // this.b = undefined
//5 and 6: using self
logMessage("self.a = " + self.a); // self.a = undefined
logMessage("self.b = " + self.b); // self.b = 2
}
}
Now, I understand that a reference to a
directly works.
I also understand that messages 3 and 4 (this.a
and this.b
) will fail because this
refers to the internal function. I also understand that line 6 works because I save the reference to the original object.
What I do not understand is:
- why aren't messages 1 and 2 working alike?
- why aren't messages 5 and 6 working alike?
I can't seem to get my head around a specific case of scoping for JavaScript variables. Different from other examples and questions I have found, I am interested in the scoping for nested functions.
I've set up an example at this JSFiddle. The relevant part is the following:
function MyObject() {
var self = this;
var a = 1;
this.b = 2;
var innerMethod = function() {
//1 and 2: direct reference
logMessage("a = " + a); // a = 1
//logMessage("b = " + b); // Error: b is not defined
//3 and 4: using this
logMessage("this.a = " + this.a); // this.a = undefined
logMessage("this.b = " + this.b); // this.b = undefined
//5 and 6: using self
logMessage("self.a = " + self.a); // self.a = undefined
logMessage("self.b = " + self.b); // self.b = 2
}
}
Now, I understand that a reference to a
directly works.
I also understand that messages 3 and 4 (this.a
and this.b
) will fail because this
refers to the internal function. I also understand that line 6 works because I save the reference to the original object.
What I do not understand is:
- why aren't messages 1 and 2 working alike?
- why aren't messages 5 and 6 working alike?
-
5
Why would they work alike? Seems like you're analogizing with Java or some other language where the
this
namespace is implicit, which is not the case for JS. – Fabrício Matté Commented Feb 23, 2013 at 23:28 - @FabrícioMatté I may be unconscious doing this (pun intended). I fail to understand if the scoping should make variables/members automatically exposed to inner members. It seems that it is not the case, because it's not consistent. – Alpha Commented Feb 23, 2013 at 23:33
-
3
@Alpha You're paring variables (
var a = 5;
) with things that are not variables but properties of objects (this.b = 10;
sets the propertyb
of the object thatthis
refers to to10
). These things are not the same and therefore do not behave identically. – Niko Commented Feb 23, 2013 at 23:35 - I'm thinking about a simple way to explain it, but basically JS's lexical scope won't automatically expose members to inner members as you've said. – Fabrício Matté Commented Feb 23, 2013 at 23:36
-
1
@Alpha see how 4 changes if you did
innerMethod.call(this);
orthis.method = innerMethod;
and(new MyObject()).method();
– Paul S. Commented Feb 23, 2013 at 23:38
3 Answers
Reset to default 4The a
variable is just that, a variable. It's visible in the scope of innerMethod
(which is just a nested function), as a
, which is how it was declared (ie. JavaScript has lexical scoping rules, inner functions can see variables of the functions they're defined inside of).
this
isn't the same as the local scope of the MyObject
constructor.
You've seen that self
is an alias for this
of MyObject
, and that innerMethod
has overwritten this
in its own scope. Still, since this
is not an alias for function scope, neither self.a
nor this.a
will ever work here.
For a more rigorous explanation of lexical scoping you can e.g. start at wikipedia: http://en.wikipedia/wiki/Scope_(puter_science)
You can read about the execution contexts and identifier resolution rules in the ECMA standard http://es5.github./#x10.3
It's a problem with scope, when functions are created they save their surroundings (including variables).
So when innerMethod
is created, it can see variables self
and a
.
An important concept is that the scope is created when the function is declared, instead of when it is called.
In your case 1, b
is not being declared (the this object is not the same).
In cases 5 and 6, you did not create self.a
.
The main reason is that self
is not equal to this
in scope of innerMethod. this
is a keyword to reference the owner of the function. For innerMethod, it is NOT a instance method, it belongs to the Window.
function MyObject() {
var self = this;
var innerMethod = function() {
alert("inner method, self == this?: " + self == this); // false
alert("inner method: " + this); // [object Window]
alert("closest constructor name of in prototype chain ?: "+ this.__proto__.constructor.name); // Window
}
this.outerMethod = function(){
innerMethod();
alert("outer method: " + this); // [object MyObject]
alert("closest constructor name in prototype chain?: "+ this.__proto__.constructor.name); // MyObject
}
}
var o = new MyObject();
o.outerMethod();
You can play at here