FK82
FK82

Reputation: 5075

How does the Javascript "this" operator deal with scopes?

Edit: I have to apologize, the problem I posted about actually does not exist in the code I posted, because I oversimplified it. I'll try to post something later.

Deletion would also be ok, but there are too many answers at present, so I cannot do it myself.

Edit2: Ok, here goes:

Let,

function F() {

    this.field = "value" ;

    var init = function(value) {
        this.field = value ;
    } ;

    this.method = function() {
        return this.field ;
    } ;

    init( arguments[0] ) ;

} 

Now, instantiation of F type,

var f = new F("newValue") ;

will set the value to the Window object as this points to it when called from the closure.

Binding this to the Function,

function F() {

    var self = this ;

    this.field = "value" ;

    var init = function(value) {
        self.field = value ;
    } ;

    this.method = function() {
        return this.field ;
    } ;

    init( arguments[0] ) ;

}

will resolve the problem.

Still, what is the reason for this -- imho -- odd behaviour?

Upvotes: 2

Views: 402

Answers (5)

FK82
FK82

Reputation: 5075

As it turns out, this is one of the dark corners of the ECMA specification for ECMAScript (hence JavaScript):

Finally a value is assigned for use with the this keyword. If the value assigned refers to an object then property accessors prefixed with the this keyword reference properties of that object. If the value assigned (internally) is null then the this keyword will refer to the global object.

(http://jibbering.com/faq/notes/closures/ ; thanks to @ Brian Flanagan for the link)

Apparently, during execution of a function that is assigned to a variable (within another Function) the context is lost, the scope is thus set to null and this will refer to the Window Object.


I'm not quite sure whether this is something to expect from a Function initialized as a local variable. Typically I would expect a closure to have access to the scope it was created while that is still in memory, including it's predecessor in the scope chain.

This behaviour is different from a function that is the property of a Function Object, where the context is not lost and this will correctly point to the owner of the member Function (which is the Function Object or a member depending on how many layers there are).


The solution for this problem (while maintaining private Function members, i.e. members that are only accessible from within the Function's scope) is wrapping the function inside another Function using Function.prototype.apply (or Function.prototype.call) to set the context manually:

function F() {

    this.field = "value" ;

    var self = this ;

    var init = function(value) {

        (function() {
            this.field = value ;
        }).apply(self,value) ;

    } ;

    this.method = function() {
        return this.field ;
    } ;

    init( arguments[0] ) ;

} 

This is similar to Function.prototype.bind introduced in JavaScript 1.85 as well as the PrototypeJS library.

Edit: Forgot parentheses around the enclosed function in init. This will result in a SyntaxError.

Upvotes: 1

letronje
letronje

Reputation: 9148

Is this the example you were looking for ?

var F = function() {

     var self = this ; //!! points correctly to the Function Object

     this.field = "someValue" ;

     var helper1 = function(){
         return self.field;
     }
     var helper2 = function(){
         return this.field;  
     } 
     this.method = function(){
         return helper1();
     }
     this.method2 = function(){
         return helper2();
     }
 }

 var f = new F() ;
     console.log(f.method()) ; 
     console.log(f.method2()) ; 

In the case of method, it calls helper which uses self, which points to f when it gets created and hence it works.

In the case of method2, it uses helper2, which uses this. But the meaning of this while inside helper2 is the 'global' scope and hence returns undefined.

Upvotes: 1

Jeremy Elbourn
Jeremy Elbourn

Reputation: 2700

For the defining of method, you probably want to do something more like:

this.method = function()
{
    return this.field;
}

Upvotes: 1

DVK
DVK

Reputation: 129529

This is (a reference to) the object on which a function was called.

So in your f.method() call, this is "f". NOT "F".

The fact that var self = this; works is not due to this statement itself, but due to the fact that method2() is a closure

To be more specific, the variable self inside method2() has a scope determined by F() - in other words, "self" inside method2() will ALWAYS refer o the value of self which existed when "F" was defined (which, at the time, was of course "F" since at that point current object context was "F").

Upvotes: 2

Pointy
Pointy

Reputation: 413996

It's not correct to call this an "operator". It's an object reference established by the runtime upon function activation.

Javascript is just a different language that does things in different ways. That this is under control of the programmer is wonderfully powerful.

Also that code definitely does not work unless something else you didn't post is setting up a prototype object for "F". Neither "method" nor "method2" are callable from a reference to an instance of "F".

Upvotes: 1

Related Questions