Can I "extend" a closure-defined "class" in Javascript?
Asked Answered
B

3

16

I have a Javascript "class" defined like so:

var Welcomer = function(name) {
  var pName = name;
  var pMessage = function() {
    return "Hi, " + pName + "!";
  };

  return {
    sayHi: function() {
      alert(pMessage());
    }
  };
};

new Welcomer('Sue').sayHi();

Is there a way to "subclass" Welcomer in such a way that I can redefine the public methods and have access to the private methods and variables? The following will give me access to the public methods, but not to the private ones:

var UnhappyWelcomer = function(name) {
  var pSadMessage = function() {
    // won't work, b/c I don't have access to pMessage
    return pMessage() + " Miserable day, innit?";
  };

  return {
    sayHi: function() {
      alert(pSadMessage());
    }
  };
};
UnhappyWelcomer.prototype = Welcomer(); // failed attempt at inheritance

new UnhappyWelcomer().sayHi();
Bebe answered 24/8, 2010 at 21:27 Comment(2)
You might want to have a read over this article.Stopwatch
Bookmarking that article. ThxClub
E
10

The simple answer to your question is No.

You can't do anything to gain access to those var defined things, unless your function is in the same scope, or you somehow 'make public' the information (by returning it, or setting it on this).

If you have access to edit the original function though, you could rewrite things in such a way that those functions could be "passed" into an extending function, which can alter the "protected" scope of the object. The following code should give a good idea of what I am proposing.

var Welcomer = function(name) {
  var _protected = {
    name: name,
    message: function() {
      return "Hi, " + _protected.name + "!";
    }
  };

  return {
    extendWith: function(extender) {
      extender.call(this, _protected);
      return this;
    },
    sayHi: function() {
      alert(_protected.message());
    }
  };
};

var UnhappyWelcomer = function(name) {
  var self = Welcomer(name).extendWith(function(_protected) {
    _protected.sadMessage = function() {
       return _protected.message() + " Miserable day, innit?";
    };
    // extending the public while still having scope to _protected...
    this.sayHi = function() {
      alert(_protected.sadMessage());
    };
  });
  return self;
};

UnhappyWelcomer('bob').sayHi();
Erma answered 24/8, 2010 at 21:39 Comment(1)
I certainly do have access to the original function, so I'd love to write it in such a way to make things "protected" instead of "private." Can you provide an example?Bebe
G
8

That "class" pattern is not a "class" pattern, it's known as a Module Pattern. It returns an object that has no ties to the function that created it other then availability of it's private variables. The returned object is NOT an instance of the function that created it.

When you call 'new Class()' it does create an instance of that function, but it also returns another object. Any further operations is actually on the returned object and not the instance.

In order to use inheritance, you really need to use prototypical inheritance properly, I suggest you read:

Further reading:

Sorry to leave you with reading material, but it seems to me you are exploring possibilities. These articles will give a deeper insight on the matter.

Once you know more on the matter, you will most likely ignore private members alltogether and use the _ prefix and just make it a public member like everyone else ;) It's just easier and private members are pointless anyways.

Gamester answered 24/8, 2010 at 21:56 Comment(0)
M
1

If you really need inheritance, there are some libraries out there that will help immensely. One option is trait.js, which may actually become a standard part of javascript at some point anyway. If that doesnt float your boat, libraries like jQuery and prototype have helpers for inheritance.

If you want to go with a minimal/from-scratch approach, I strongly suggest you use the prototype model for everything. You will see a large performance increase over the pattern you have in your examples.

To more directly address your question, no. You will have an easier time if you design your 'classes' so private functions aren't relied upon.

Mellissamellitz answered 24/8, 2010 at 21:38 Comment(2)
I tried return jQuery.extend({}, Welcomer(), { sayHi: function() { alert(pSadMessage()); });, but extend apparently only copies the elements defined in the object literal (the public methods), not the closure scope.Bebe
Yea, the way you have designed your "class" with the private var/functions defined in the constructor, they just arnt going to be visible, which is why I would definitely move to a more standard prototype based structureMellissamellitz

© 2022 - 2024 — McMap. All rights reserved.