JavaScript local scoping: var vs. this
Asked Answered
S

3

9

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?
Supertanker answered 23/2, 2013 at 23:23 Comment(8)
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.Emmittemmons
@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.Supertanker
@Supertanker You're comparing variables (var a = 5;) with things that are not variables but properties of objects (this.b = 10; sets the property b of the object that this refers to to 10). These things are not the same and therefore do not behave identically.Baron
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.Emmittemmons
@Baron Thanks! I think that was the very core of my confusion.Supertanker
@Supertanker see how 4 changes if you did innerMethod.call(this); or this.method = innerMethod; and (new MyObject()).method();Marinate
@PaulS. Changing the this reference is a little out of the scope of the question I believe, but here's a good article if OP or anyone wants to read: Mythical methods by TJ Crowder.Emmittemmons
See also https://mcmap.net/q/304178/-javascript-do-i-need-to-put-this-var-for-every-variable-in-an-object/1048572Spiculum
B
4

The 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.org/wiki/Scope_(computer_science)

You can read about the execution contexts and identifier resolution rules in the ECMA standard http://es5.github.com/#x10.3

Baguio answered 23/2, 2013 at 23:31 Comment(2)
I'd prefer the term VariableEnvironment instead of function scope in your 3rd paragraph, and state that the reason behind the first paragraph is that JS has lexical scope. But very well explained.Emmittemmons
I've incorporated some of your changes, but I'm hesitant to exchange VariableEnvironment for function scope. Even though it is technically more correct, I believe function scope is a concept that is better known for programmers coming from other languages. I have added your link to the ECMA standard.Baguio
S
1

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.

Stationer answered 23/2, 2013 at 23:29 Comment(0)
L
0

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

Limbic answered 5/2, 2015 at 11:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.