24

I define the following MyClass and its methods in a user script:

function MyClass() {
    this.myCallback = function() {
        alert("MyClass.myCallback()");
    };

    this.startRequest = function() {
        GM_xmlhttpRequest({
            'method': 'GET',
            'url': "http://www.google.com/",
            'onload': function (xhr) {
                myClassInstance.myCallback();
            }
        });
    };
}

var myClassInstance = new MyClass();
myClassInstance.startRequest();

This script works and the myCallback() method gets called once the GM_xmlhttpRequest completes.

However, it only works because the onload callback is referring to the global variable myClassInstance. If I update the onload callback to:

'onload': function (xhr) {
    this.myCallback();
}

Then I get the (Chrome) error:

Uncaught TypeError: Object [object DOMWindow] has no method 'myCallback'.

It seems this is being evaluated in the wrong context.

Is there a way to invoke the myCallback() method of myClassInstance without having to resort to using a global variable?

0

4 Answers 4

40

Save the correct this, when it is in scope, into a variable. Then you can reference it later:

 this.startRequest = function() {
     var myself = this;
     GM_xmlhttpRequest({
         'method': 'GET',
         'url': "http://www.google.com/",
         'onload': function (xhr) {
             myself.myCallback();
         }
     });
 };
Sign up to request clarification or add additional context in comments.

1 Comment

+1 You just ended my hour long head slamming session. Thank you. :)
8

The simplest solution, as already was pointed out, is creating an alias for the this that stays in scope. The most popular variable names for the alias are things like self or that, but anything works, really.

The other alternative (that might or might not be better, depending on the use case) is binding the method into a "plain" function and using that instead:

var f = this.callback.bind(this);

...
'onload': function(){
    f();
}

bind is not supported on old browsers, but you can find alternatives in many JS frameworks. The example I gave doesn't look that good, but it can be very convenient when you want to pass your method directly as a callback function (you might also get partial function application as well and that is very neat)

1 Comment

Be careful with self because of window.self.
3

Store a reference to the instance and use it:

function MyClass() {
    this.myCallback = function() {
        alert("MyClass.myCallback()");
    };

    var instance = this;

    instance.startRequest = function() {
        GM_xmlhttpRequest({
            'method': 'GET',
            'url': "http://www.google.com/",
            'onload': function (xhr) {
                instance.myCallback();
            }
        });
    };
}

Comments

0

With modern javascript, this is more elegantly solved with arrow functions which preserve the scope of this

function MyClass() {
    this.myCallback = function() {
        alert("MyClass.myCallback()");
    };

    this.startRequest = function() {
        GM_xmlhttpRequest({
            'method': 'GET',
            'url': "http://www.google.com/",
            'onload': (xhr) => {
                this();
            }
        });
    };
}

var myClassInstance = new MyClass();
myClassInstance.startRequest();

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.