Question

I spent the better part of the day reading about the module pattern and its 'this' scope. Eventually I found a work-around for my problem, although with a feeling there's a better way of doing things.

The actual code is >200 lines, but I've boiled it down to the following: objA has a method (publicA) that objB wants invoke by callback. The detail that complicates things is that publicA needs help from publicA_helper to do its job. (http://jsfiddle.net/qwNb6/2/)

var objA = function () {
    var privateA = "found";
    return {
        publicA: function () {
            console.log("privateA is " + this.publicA_helper());
        },
        publicA_helper: function () {
            return privateA;
        }
    };
}();

var objB = function () {
    return {
        callback: function (callback) {
            callback();
        }
    }
}();

objA.publicA(); // privateA is found
objB.callback(objA.publicA); // TypeError: Object [object global]

Fair enough – I've grasped that the caller's context tends to influence the value of 'this'. So I add measures to retain 'this' inside objA, of which none seems to work. I've tried the var objA = (){}.call({}) thingy, setting var self = this; (calling self.publicA_helper() accordingly). No luck.

Eventually, I added a private variable var self;, along with a public method:

init: function() {self = this;},

...and by making sure I call objA.init(); before passing objA.publicA to objB.callback, things actually work.

I cannot stress the immensity of the feeling that there's a better way of doing this. What am I missing?

Was it helpful?

Solution 2

I've tried the var objA = (){}.call({}) thingy,

How? You want to use call on the callback that you want to invoke with a custom this, not on your module closure. It should be

var objB = {
    callback: function (callback, context) {
        callback.call(context);
    }
};

objB.callback(objA.publicA, objA);

I've tried setting var self = this;

The self variable is supposed to be in a closure and point to the object on the methods are stored. That is only this when your module IEFE would be invoked on your module - it's not. Or if it was a constructor - it's not. You could change that with call as above:

var objA = function () {
    var privateA = "found",
        self = this;
    this.publicA = function () {
        console.log("privateA is " + self.publicA_helper());
    };
    this.publicA_helper = function () {
        return privateA;
    };
    return this;
}.call({});

But that's ugly. In your case, the self variable simply needs to point to the object literal which you're returning as your module:

var objA = function () {
    var privateA = "found",
        self;
    return self = {
        publicA: function () {
            console.log("privateA is " + self.publicA_helper());
        },
        publicA_helper: function () {
            return privateA;
        }
    };
}();

Btw, since you're creating a singleton you don't need an explicit self, you could just reference the variable that contains your module (as long as that doesn't change):

var objA = function () {
    var privateA = "found";
    return {
        publicA: function () {
            console.log("privateA is " + objA.publicA_helper());
        },
        publicA_helper: function () {
            return privateA;
        }
    };
}();

Another method would be to simply make all functions private and then expose some of them - by referencing them local-scoped you will have no troubles.

var objA = function () {
    var privateA = "found";
    function publicA() {
        console.log("privateA is " + helper());
    }
    function helper() {
        return privateA;
    }
    return self = {
        publicA: publicA,
        publicA_helper: helper // remove that line if you don't need to expose it
    };
}();

OTHER TIPS

The generalized solution is extremely simple.

Write all the module's methods as private, then expose those that need to be public.

I write all my modules this way :

var objA = function () {
    var privateA = "found";
    var A = function () {
        console.log("privateA is " + A_helper());
    },
    var A_helper = function () {
        return privateA;
    }
    return {
        publicA: A
        //A_helper need not be exposed
    };
}();

Thus, all methods are in the same scope, each one having direct access to all other methods in the same module, and the ambiguous this prefix is avoided.

objB.callback(objA.publicA); will now work as expected.

See fiddle

The reason is that the context is getting changed when you are invoking the callback. Not a generalized solution, but shows that the code works by specifying the context while invoking callback.

var objA = function () {
  var privateA = "found";
  return {
    publicA: function () {
        console.log("privateA is " + this.publicA_helper());
    },
    publicA_helper: function () {
        return privateA;
    }
  };
}();

var objB = function () {
    return {
        callback: function (callback) {
          callback.call(objA);
        }
   }
}();

objA.publicA(); // privateA is found
objB.callback(objA.publicA); // privateA is found
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top