jskidd3
jskidd3

Reputation: 4783

Can't access object property when callback is invoked

function Push(apiInstance) {
    this.api = apiInstance; // If I alert() here this is a valid object
    this.pushNotification = window.plugins.pushNotification;

    if ( device.platform == 'android' || device.platform == 'Android' )
    {
        this.pushNotification.register(
            this.successHandler,
            this.errorHandler, {
                "senderID":"",
                "ecb":"onNotificationGCM"
            });
    }
    else
    {
        this.pushNotification.register(
            this.tokenHandler,
            this.errorHandler, {
                "badge":"true",
                "sound":"true",
                "alert":"true",
                "ecb":"onNotificationAPN"
            });
    }
}

Push.prototype.tokenHandler = function (token) {
    this.api.registerDevice(token); // Suddenly this.api is undefined
};

var push = new Push(/* in my production code this is a valid object */);

I know that apiInstance is valid as if I alert the instance in the constructor it shows as an object. For some reason when the tokenHandler callback is invoked this.api is suddenly undefined.. any ideas why? OOP in JavaScript is so frustrating...

Upvotes: 1

Views: 67

Answers (3)

dimko1
dimko1

Reputation: 872

That is because scoping in javascript is done on the functions level.

In this peace of code:

function Push(apiInstance) {
    this.api = apiInstance; // If I alert() here this is a valid object
}

"this" - is the scope of the function Push. so, you can try to do next:

var api = {};
function Push(apiInstance) {
    api = apiInstance; // If I alert() here this is a valid object
    this.pushNotification = window.plugins.pushNotification;

    if ( device.platform == 'android' || device.platform == 'Android' )
    {
        this.pushNotification.register(
            this.successHandler,
            this.errorHandler, {
                "senderID":"",
                "ecb":"onNotificationGCM"
            });
    }
    else
    {
        this.pushNotification.register(
            this.tokenHandler,
            this.errorHandler, {
                "badge":"true",
                "sound":"true",
                "alert":"true",
                "ecb":"onNotificationAPN"
            });
    }
}

Push.prototype.tokenHandler = function (token) {
    api.registerDevice(token); // Suddenly this.api is undefined
};

var push = new Push(/* in my production code this is a valid object */);

In this code api - will be variable not in scope of the function, but in "global scope" for the function Push.

Upvotes: 0

Andrei
Andrei

Reputation: 56726

The problem is that this:

this.tokenHandler

is just a function itself, and the function does not know anything about where it belongs to and what it's context should be. To work this around you can apply the following:

var self = this;
this.pushNotification.register(
    function (token) {
        self.tokenHandler(token);
    },

This use of a closure will ensure that tokenHandler is called with a self context, which is an instance of a Push

Upvotes: 1

thefourtheye
thefourtheye

Reputation: 239663

When you do

this.pushNotification.register(
    this.tokenHandler,
    this.errorHandler, ...);

You are essentially passing the function object tokenHandler and it doesn't know that it was bound to the current object. So when it is invoked by the register function, as it is not bound to any object, JavaScript will pass the global object as this (in Strict mode it will pass undefined). To avoid that, you need to explicitly bind the current object with the function object, with Function.prototype.bind like this

this.pushNotification.register(
    this.tokenHandler.bind(this),
    this.errorHandler.bind(this), ...);

Upvotes: 1

Related Questions