Question

I have some javascript using the Revealing Module Pattern as shown below. It accepts a callback function that it later invokes. I want that callback function to then be able to call functions defined in the class, but it's not working.

window.MyClass = function() {
  var self = this,

  start = function (callback) {
    callback(self);
  },

  cancel = function() {
    console.log('Cancel invoked');
  };

  return {
    start: start,
    cancel: cancel
  };
};

var myCallbackFunction = function(instance) {
  instance.cancel(); // Error: instance.cancel is not a function
};

var obj = new window.MyClass();
obj.start(myCallbackFunction);

I can rework this sample into the Revealing Prototype Pattern and it works as expected, so my question is can I get this working using RMP, or is it just a limitation of this pattern?

Thanks, Roger

Was it helpful?

Solution

window.MyClass = function() {
  var self = this;

  this.start = function (callback) {
    callback(self);
  };

  this.cancel = function() {
    console.log('Cancel invoked');
  };

  return {
    start: this.start,
    cancel: this.cancel
  };
};

Then, it's both an instance method, and available via the returned value.

But for something this simple (e.g. no private variables), I would use a straight-up prototype ("Prototype Pattern").

By the way, if you're reading this series, you should be aware these are not really standard terms.

He also stated, "Although JavaScript isn’t designed with the concept of classes or object oriented programming in mind as with C# or Java, with a little work you can achieve similar results." In fact, JavaScript is designed for object-oriented programming, but it's prototype-based. It's also flexible enough to allow alternatives, as you've seen; those alternatives are definitely better in some cases.

You don't even need self, since your objects stay bound.

So:

window.MyClass = function(){};

window.MyClass.prototype = {
    start: function (callback) {
        callback(this);
    },

    cancel: function () {
        console.log('Cancel invoked');
    }
};


var myCallbackFunction = function(instance) {
  instance.cancel();
};

var obj = new window.MyClass();
obj.start(myCallbackFunction);

OTHER TIPS

Never return an object from a constructor. This is one of those weird JS quirks.

You aren't getting an instance of MyClass, you are getting a plain object. When a contructor returns an object, that object is returned instead of the instance that would have been returned.

// good!
function MyClass() {};
new MyClass().constructor // returns: function MyClass() {}
new MyClass().constructor == MyClass // true

// bad!
function MyClass() { return {a:123}; };
new MyClass().constructor // returns: function Object() { [native code] }
new MyClass().constructor == MyClass // false

In short, classical style and revealing module style don't mix well.

In your case, any function assign to a property of this is you revealing that function to the outside. So maybe you wanted this instead.

window.MyClass = function() {
  var self = this;

  var start = function (callback) {
    callback(self);
  };

  var cancel = function() {
    somePrivateFn();
    console.log('Cancel invoked');
  };

  var somePrivateFn = function() {
    console.log('private function!');
  };

  this.start = start;
  this.cancel = cancel;
};

But if you don't need anything private (your example reveals everything, so nothing remains private) use a prototype based approach like Matthew suggests, which is faster and simpler in all regards.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top