Question

I'm building a visualization page (with dc.js) for which I decided to make the jump and gather it all into a single namespace. Coming from Python's simplicity, crashing against JavaScript's scope madness has been tough enough, so please bear with me.

I have a general JS structure as follows:

var NamespaceClass = function() {

    this.var0 = "something";
    this.var1 = dc.SomeChartClass("#some-css-selector");

    this.setup = function(error, config, dataset) {
        console.log("Inside setup:", this);
        this.var2 = this.process_data(dataset);
        // Do some more stuff...
    }

    this.process_data = function(data) {
        var whatever;
        //Do stuff with "data"...
        return whatever;
    }

    this.start = function() {
        console.log("Inside start:", this);
        var q;

        q = queue().defer(d3.json, "config.json")
                   .defer(d3.csv, "data.csv");
        q.await(this.setup);
    }
}

var MyNamespace = new NamespaceClass();
MyNamespace.start();

where queue is Mike Bostock's queue lib for asynchronous file queueing. When I try to test the script, I get in the console:

Inside start: Object { var0 = "something", var1={...}, more...}
Inside setup: Window testpage.html
TypeError: this.process_data is not a function

So, invoking setup from q.await makes it loose the object's scope (or whatever this is called in JavaScript...). How can I avoid this? I have also tried using a proxy object like:

    this.start = function() {
        console.log("Inside start:", this);
        var q, proxy;

        q = queue().defer(d3.json, "config.json")
                   .defer(d3.csv, "data.csv");
        proxy = this.setup;
        q.await(proxy);
    }

to no avail!

Was it helpful?

Solution

The value of this is determined by how you call the function that contains it. You have no control over how the setup function is called, since it is called inside the q.await function.

When you do q.await(this.setup), you pass a reference to the function setup, but "lose" any connection to your namespace. You need to bind the function to the namespace for it to have the proper this value:

q.await(this.setup.bind(this));

Even better, since all your functions work only if this is the namespace, you should bind them to the namespace like this:

this.start = function() {
  // body
}.bind(this);

// usage:
q.await(this.setup);

And now wherever you pass them, they will have the correct this inside.

Note: in this situation, bind works something like this:

this.start = function() {
    var namespace = this;

    q.await(function(){ namespace.setup(); });
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top