Gaz
Gaz

Reputation: 328

Can someone please tell me why I'm not understanding scope in this example?

You'll have to forgive me -- I'm a seasoned Java programmer, but still struggling with the semantic differences between the languages!

I have a small JavaScript object constructor. It includes a jQuery function which gets a small piece of Ajax data. When the data gets returned, I want to make use of it in conjunction with a variable (renderMode) defined at the top of the object constructor but, presumably because of function block closure, I am getting 'undefined'.

  1. Can someone give me a corrected piece of code which shows me how to access renderMode inside of function(data)
  2. Almost as important, can someone explain to me why what I did doesn't work !

Here's the example:

function AsyncRequest() {
    this.renderMode = RenderMode.NONE;
    this.setId = setId;

    function setId(v) {
        this.id = v;
    }
    this.setURL = setURL;

    function setURL(v) {
        this.URL = v;
    }

    this.render = render;

    function render(element) {
        $.get(
            this.URL,
            function (data) {
                // The data comes back just fine ...
                // ... and I know a value was previously assigned to renderMode
                // ... in this instance of AsyncRequest, but how do I get at it ?
                console.log("Render mode: " + this.renderMode); // this.renderMode is undefined
            }
        );
    }
}

Upvotes: 2

Views: 81

Answers (2)

Giovanni Mesquita
Giovanni Mesquita

Reputation: 165

You can create a var for "this" in the main function:

function AsyncRequest() {
    var self = this;
    self.renderMode = RenderMode.NONE;
    self.setId = setId;
    ...

    function render(element) {
        $.get(
            self.URL,
            function (data) {
                console.log("Render mode: ", self.renderMode);
            }
        );
    } 
}

Upvotes: 0

T.J. Crowder
T.J. Crowder

Reputation: 1074138

Unlike in Java, this in JavaScript is determined primarily by how a function is called, not where it's defined. (ES6 will add a way to have this lexically-bound, but for now it's primarily bound by the function call.) So the callback you're passing into $.get is getting called with this not referring to the object you expect.

You can solve this by using the fact you have a closure:

function render(element) {
    var self = this;  // <=== Note
    $.get(
        this.URL,
        function (data) {
            console.log("Render mode: " + self.renderMode);
            // Note ----------------------^
        }
    );
}

There, we're remembering what this was when render was called in a variable called self (the name doesn't matter), and then using the fact that the callback function is a closure over the context of the call to render, and so we have access to the self variable.

You can also solve it with ES5's Function#bind:

function render(element) {
    $.get(
        this.URL,
        function (data) {
            console.log("Render mode: " + this.renderMode); // this.renderMode is undefined
        }.bind(this) // <== Note
    );
}

Function#bind returns a function that, when called, calls the original function with this set to the value you give it.

More (on my blog):

Upvotes: 2

Related Questions