views:

208

answers:

4

I've got a problem concerning the javascript "this" keyword when used within a javascript functional object. I want to be able to create an object for handling a Modal popup (JQuery UI Dialog).

The object is called CreateItemModal. Which i want to be able to instantiate and pass some config settings. One of the config settings. When the show method is called, the dialog will be shown, but the cancel button is not functioning because the this refers to the DOM object instead of the CreateItemModal object.

How can I fix this, or is there a better approach to put seperate behaviour in seperate "classes" or "objects". I've tried several approaches, including passing the "this" object into the events, but this does not feel like a clean solution.

See (simplified) code below:

function CreateItemModal(config) {
    // initialize some variables including $wrapper
};

CreateItemModal.prototype.show = function() {
    this.$wrapper.dialog({
        buttons: {
            // this crashes because this is not the current object here
            Cancel: this.close
        }
    });
};

CreateItemModal.prototype.close = function() {
    this.config.$wrapper.dialog('close');
};
+2  A: 

try this:

CreateItemModal.prototype.show = function() {
    var me = this;
    this.$wrapper.dialog({
        buttons: {
            // this crashes because this is not the current object here
            Cancel: me.close
        }
    });
};

The reason why it doesn't work, because the "this" is referring to the dialog, not to that class.

Hery
Lots of upvotes on an incorrect answer brilliant ;P. The code above changes nothing. The close function still executes in the context of button element not the `CreateItemModal` instance.
AnthonyWJones
Correct, just tested it a minute ago, and this will not work unfortanately.
Rody van Sambeek
That's right, because `me.close` is stored in `Cancel` as a function: its containing object `me` is lost. So `this` is `window` when executing it.
Alsciende
A: 

Try to add a variable that is equal to global this e.g

function CreateItemModal(config) {
    // initialize some variables including $wrapper
};

CreateItemModal.prototype.show = function() {
    var $this = this;
    this.$wrapper.dialog({
    buttons: {
        // this crashes because this is not the current object here
        Cancel: $this.close
    }
});

As for me, it works in most cases

Mikhail
+4  A: 

You need to create a closure to trap the this context, I tend to use an anonymous function to do this as follows:-

CreateItemModal.prototype.show = function() {
    this.$wrapper.dialog({
        buttons: {
            // this crashes because this is not the current object here
            Cancel: (function(self) {
              return function() { self.close.apply(self, arguments ); }
            })(this);
        }
    });
};
AnthonyWJones
This works, however is this a "best practice"? It seems like a lot of overhead to just let javascript see this as a real "OO" object?
Rody van Sambeek
Well, that's because you're using jQuery, and jQuery is not made to help you write that kind of javascript. With most other frameworks, you would just write `Cancel: this.close.bind(this)`
Alsciende
@Rody: This is the only practice. ECMAScript 5th Edition introduces the function prototype method `bind`, which will allow you to do something like this: `Cancel: this.close.bind(this);`. There are implementations of the `bind` prototype around on the web if you have to do this sort of thing many times throughout your code.
Andy E
@Andy E: Thanks for clearing it for me. Will look into the closure behaviour of javascript, seen this a couple of times but never really dove into it.
Rody van Sambeek
@Alsciende: Just to be clear the overhead of a closure is not eliminated by frameworks that offer `bind` since internally they'll use a closure to implement it.
AnthonyWJones
@AnthonyWJones: Sure, but it's a lot less overhead for the developer to just call `.bind(this)` on a function than to manually set up a closure.
Alsciende
@Alciende: Agreed. Its not rocket since to add such an extension ourselves without the assistance of a framework if we got really fed up doing the same code over and over. I often find though that the closure function needs to do some other slightly different thing before calling into other functions. In this particular case the close on the prototype is probably superflous its code could be placed directly in the enclosed function.
AnthonyWJones
@AnthonyWJones @Rody prototype.js has a good bind() implementation that is easy to borrow. http://www.prototypejs.org/api/function/bind. Even for one off's var callback = (function(arg1) { this.dothis(arg1); //etc }).bind(this); callback(); reads alot cleaner imo. Anything to get rid of `self` or `that` not-so-much-keyword-variables :)
Martijn Laarman
+2  A: 

Everyone who encounters problems with "this" in JavaScript should read and digest this blog post: http://howtonode.org/what-is-this

You would also do well to Google "Douglas Crockford" and watch some of his (free) videos on the subject.

mikehatfield