2

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!

Luis E.
  • 841
  • 11
  • 15
  • This is not related to your problem, but there is no need to create a class to have a namespace. A simple object will do, you can initialize it with an IIFE to have all your variables scoped so they don't pollute the global scope. – Tibos Feb 11 '14 at 14:29
  • This has been the only way so far that has allowed me to perform inter-class method calls (like `this.fun1` calling `this.fun2`). But it's rather tedious using `this.` every time. Would you care to point to me to some examples of what you're suggesting? – Luis E. Feb 11 '14 at 14:51
  • This [answer](http://stackoverflow.com/a/5647397/1669279) has an example. – Tibos Feb 11 '14 at 14:57
  • I tried that way, but it wouldn't allow me to have class functions (methods) call other methods on their own. – Luis E. Feb 11 '14 at 15:05

1 Answers1

2

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(); });
}
Tibos
  • 27,507
  • 4
  • 50
  • 64
  • I had to use `.bind(namespace)` (where `namespace` is as per your last example) for anonymous functions declared within fluent methods (like those of `dc.js`) inside of class methods. – Luis E. Feb 11 '14 at 15:04