1

I'm using John Resig's excellent javascript class for simple javascript inheritance and I'm having a spot of bother getting my head around some variable scope problems in relation to the 'this' keyword.

My extended class looks a little like this

var EUNode = Class.extend({

    // Methods
    //-------------------------------------------------------------------------------
    init : function(){

        this.div = this.createDiv();
        this.canDoStuff = true;
        this.div.addEventListener(touchstart, this.touchStartHandler, false);

    },



    // Create Div
    //-------------------------------------------------------------------------------
    createDiv : function(){

        // code to create a DIV element, add some classes and do some 
        // other useful stuff

    },

    touchStartHandler : function(event){

        // In here, 'this' refers to the div element, so the 
        // following condition doesn't work
        if(this.canDoStuff){

            // do some stuff

        }

    }

});

I need to be able to reference the EUNode instance from inside the touchStartEventHandler, so I tried the following

var EUNode = Class.extend({

    // Methods
    //-------------------------------------------------------------------------------
    init : function(){

            this._super();
            // Store a reference to 'this' globally
        self = this; 
        self.div = this.createDiv();
        self.canDoStuff = true;
        self.div.addEventListener(touchstart, self.touchStartHandler, false);

    },

    touchStartHandler : function(event){

        if(self.canDoStuff){

            // Now I can do stuff, but 'self' is now a global variable
                    // which isn't what I want. If there's more than one instance
                    // of EUNode, 'self' references the newest instance.

        }

    }

});

So I'm stuck in a loop. It seems that in order to limit the scope of "self" globally, I need to use the "this" keyword, but "this" means something else inside event handlers. Any tips?

4 Answers 4

2

Yes, global variables are bad (in general). As you noticed yourself, you already get in trouble if you have two EUNode instances because the last one would override the global self.

You are also making touchStartHandler dependent on some global variable, and the whole working of this function might not be so clear anymore.

Just call touchStartHandler in the right context:

init : function(){
    var self = this;  // <- local `self`

    this.div = this.createDiv();
    this.canDoStuff = true;

    // pass a closure, making `self` available inside the function
    this.div.addEventListener(touchstart, function(event) {
        self.touchStartHandler(event);
    }, false);
},

In browsers supporting ECMAScript 5 you can also use .bind() [docs] (see link for an implementation for other browsers) to bind the element this should refer to:

this.div.addEventListener(touchstart, this.touchStartHandler.bind(this), false);
Sign up to request clarification or add additional context in comments.

3 Comments

+1 I'd like to put emphasis on the fact that it is always wrong to omit the var keyword. This makes all the difference in regard to the OPs scope issue.
simple simple simple. I much prefer the 'bind()' function since it keeps the code neater. Sadly, this is specifically for a cocoa WebView implementation.
@gargantaun: Mmh. Maybe you can still extend Function.prototype. Have a look at the link. But the other way is not bad either and is common practice to solve this problem.
1

When you establish the event handler, just wrap the call to "touchStartHandler()" in an anonymous function, such that you call it through the appropriate instance of your class.

In other words:

var boundHandler = function(event) { yourInstance.touchStartHandler(event); };

Then the function "boundHandler" can be used to handle the events, and the real code will always be set up with this referencing the right object.

There's also a function in newer browsers called ".bind()" that lets you do the same thing.

Comments

1

Instead of

self.div.addEventListener(touchstart, self.touchStartHandler, false);

Try

self.div.addEventListener(touchstart, function(event) { self.touchStartHandler(event) }, false);

Comments

0

self.touchStartHandler.bind(self)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.