0

This is my javascript code Its goal is only for education. I'm studying js OOP and jquery

function App() {

this.deviceReadyDeferred = new $.Deferred();

this.init = function() {
    console.log ("Run");
    $.when(this.deviceReadyDeferred).then(this.run);
    document.addEventListener("click", this.onDeviceReady, false);

},

// NB: onDeviceReady non ha parametri evento
this.onDeviceReady = function() {
    console.log("deviceReady");
    this.deviceReadyDeferred.resolve();
},

this.run = function() {
    console.log("main");
}

}

app = new App();
app.init();

When I click, I receive

TypeError: this.deviceReadyDeferred is undefined

Why?

  • I don't receive a '$' is undefined, so jQuery is running fine.
  • I'm running jQuery 1.9.1 on FF 19.0.2 on Win 7

How to use deferred into a javascript object? How to init and reuse it ?

EDIT:

this code is working. All the problem was in my misuse of this. I'm newbie at OOP with javascript.

function App() {

    var self = this;

    this.deviceReadyDeferred = new $.Deferred();

    this.init = function() {
        console.log ("Run");
        $.when(self.deviceReadyDeferred).then(self.run);
        $(document).on("click", self.onClick);

    },

    this.onClick = function() {
        console.log("deviceReady");
        self.deviceReadyDeferred.resolve();
    },

    this.run = function() {
        console.log("main");
    }

}

app = new App();
app.init();
realtebo
  • 23,922
  • 37
  • 112
  • 189
  • 1
    You're using jQuery and its Deferred correctly. You're having trouble with the [`this` keyword](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/this) – Bergi Mar 20 '13 at 22:09
  • 2
    `this` inside of `this.onDeviceReady = function() {` is not the same as `this` outside of it. – Kevin B Mar 20 '13 at 22:11
  • @AndrewWhitaker You probably didn't click the document to trigger the error – Kevin B Mar 20 '13 at 22:13

4 Answers4

3

this inside of

this.onDeviceReady = function() {
    ...
}

is not the same as this outside of it. jQuery has a built-in way around this by passing data into the event handler.

function App() {

    this.deviceReadyDeferred = new $.Deferred();

    this.init = function () {
        console.log("Run");
        $.when(this.deviceReadyDeferred).then(this.run);
        $(document).on("click", { App: this }, this.onDeviceReady);
    },

    // NB: onDeviceReady non ha parametri evento
    this.onDeviceReady = function (e) {
        console.log("deviceReady");
        e.data.App.deviceReadyDeferred.resolve();
    },

    this.run = function () {
        console.log("main");
    }

}

app = new App();
app.init();

http://jsfiddle.net/6zAN7/17/

Or, even easier if you don't have to support IE8, use the native method (note the .bind(this)):

function App() {

    this.deviceReadyDeferred = new $.Deferred();

    this.init = function () {
        console.log("Run");
        $.when(this.deviceReadyDeferred).then(this.run);
        document.addEventListener("click", this.onDeviceReady.bind(this), false);

    },

    // NB: onDeviceReady non ha parametri evento
    this.onDeviceReady = function () {
        console.log("deviceReady");
        this.deviceReadyDeferred.resolve();
    },

    this.run = function () {
        console.log("main");
    }

}

app = new App();
app.init();

http://jsfiddle.net/6zAN7/18/

Kevin B
  • 94,570
  • 16
  • 163
  • 180
2

Other answers already explained the cause, it's the value of this inside the callback. One of the ways to solve it is by creating a new function bound to a particular this value:

document.addEventListener("click", this.onDeviceReady.bind(this), false);

Requires a shim to bind if not available natively.

bfavaretto
  • 71,580
  • 16
  • 111
  • 150
1
this.init = function() {
    console.log ("Run");
    var self = this;
    $.when(this.deviceReadyDeferred).then(self.run);
    document.addEventListener("click", self.onDeviceReady, false);

},
Adam Coulombe
  • 1,505
  • 1
  • 11
  • 11
  • Yes ! I edited my question with a copy/pasted already tested version. Tested on FF 19 – realtebo Mar 20 '13 at 22:27
  • Really? Please tell me what kind of error? I posted a working version of the code. Just tested again and working – realtebo Mar 20 '13 at 22:35
  • I think @bfavaretto is alluding to the fact that IE<=8 didn't implement `addEventListener`. As you are already using jQuery, then it would be more normal to use `$(document).on('click', self.onDeviceReady)`, which handles cross-browser differences. – Beetroot-Beetroot Mar 21 '13 at 01:10
  • @realtebo Actually, I was expecting you get the same "this.deviceReadyDeferred is undefined" when you click the document. If you only change what this answer suggests, you should still see that error. However I see from your update that you changed more, you defined `self` in a higher scope and also replaced `this` with `self` in `onDeviceReady`. That's why it's working. – bfavaretto Mar 21 '13 at 01:31
  • 1
    @Beetroot-Beetroot See the above comment. – bfavaretto Mar 21 '13 at 01:32
1

As Kevin B says, this is document inside your click handler, because you bound to that event on document in the first place.

A simple way to work around this situation is to use $.proxy():

document.addEventListener("click", $.proxy(this.onDeviceReady, this), false);
Frédéric Hamidi
  • 258,201
  • 41
  • 486
  • 479