8

Okay, my first attempt at trying to explain what I was doing failed miserably. I'm basically copying Crockford's Object.create(), except with private variables.

If you look at the accepted answer here How to inherit from a class in javascript?, you will see Object.create as the last pattern, which I think better fits the prototypal nature of Javascript (objects beget objects) instead of emulating classical inheritance (classes beget objects).

If you look at Wikipedia's article on prototype based programming (http://en.wikipedia.org/wiki/Prototype-based_programming), you can see more of what I mean.

The drawback with Object.create() though is that there is no support for private members. This is what I propose:

Function.prototype.from = function(obj) {
    function F() {this.parent = Object(obj);}
    F.prototype = obj;
    var out = new F();
    this.apply(out);
    return out;
};

Then, you create objects as thus:

// Create an object
var a = function() {
    var private_property = 'blue';
    this.public_property = 7;

    this.public_method = function() {
        alert(this.public_property + ' ' + private_property);
    }
}.from(null); // .from() works too, but .from(null) is more revealing


// Create a new object using 'a' as the prototype
var b = function() {
    var private_property = 'red';
    this.public_property = 8;
}.from(a);


// See the results
a.public_method(); // Alerts '7 blue'
b.public_method(); // Alerts '8 blue' - Parent methods use parent private variables

a.public_method = function() { alert('rabbit'); };

a.public_method(); // Alerts 'rabbit'
b.public_method(); // Alerts 'rabbit'

b.public_method = function() { alert('dog'); };

a.public_method(); // Alerts 'rabbit'
b.public_method(); // Alerts 'dog' - Parent method is overwritten

The way I made the "from" function is such that when a parent object changes its methods, if you want to prevent the change in a child instance, you can specify:

this.public_method = this.parent.public_method;

in the child instance.

Note also that objects created ex nihilo do not inherit from Object (hasOwnProperty, etc..). You must explicitly specify this as .from(Object).

Benefits of this pattern:

  1. Memory is not wasted for each new instance
  2. It adheres to a true prototypal inheritance pattern
  3. You have access to the parent object using this.parent (this.__proto__ is browser specific)
  4. Private variables now exist

There is one major drawback of this method that I can think of: the 'function()' syntax may confuse people into thinking a function is assigned to the variable instead of an object.

My question is, are there other drawbacks that I am missing? (Don't include drawbacks of the prototypal pattern--that's subjective--but only of my implementation).

Community
  • 1
  • 1
Nick
  • 5,228
  • 9
  • 40
  • 69
  • 2
    You should put up your idea as a blog post. – Corv1nus Jul 31 '10 at 00:15
  • Objects created with your "from()" function passed a `null` do indeed inherit from Object. – Pointy Jul 31 '10 at 00:16
  • Oops.. my first version of this post used \__proto\__ instead and from(null) did not inherit from Object. I've got to go now, but I'll fix that later... – Nick Jul 31 '10 at 00:23
  • @Pointy I've given up on trying to find a way to set \__proto\__ to null (without actually using \__proto\__). I cannot find a cross-browser method. I've even tried Object = undefined, but apparently it's still there when you check the constructor of a function. – Nick Jul 31 '10 at 04:00
  • Why on earth are you using Function.prototype instead of a dedicated `createChild` function that takes the constructor (and the parent object) as an argument? – user123444555621 Jul 31 '10 at 13:38
  • The same reason Crockford uses Object.create or Object.prototype.begetObject -- createChild would pollute the global namespace. – Nick Jul 31 '10 at 15:04
  • 1
    IMHO a global is not worse than an augmented Function prototype. Whatever... you could use `Object.createChild` or something, because quite frankly your `from` method is syntactic horror, as you noticed :) BTW: Don't use `Object.create`, because this will be defined in ECMAScript 5 – user123444555621 Jul 31 '10 at 18:07
  • @Nick: "this._proto_ is browser specific": in most cases, `obj.constructor.prototype` will give you the same result as `obj.__proto__`. – casablanca Jul 31 '10 at 19:15
  • @casablanca, that's usually true, however, I believe setting SomeFunction.prototype = null; and then calling var obj = new SomeFunction will type coerce (correct term?) "null" into an object. The goal was to be able to have absolutely no parent members if "null" was specified as the parent (so no hasOwnProperty, etc..). – Nick Jul 31 '10 at 20:25

1 Answers1

1

First, as already mentioned, the Function.prototype approach is really a pain. Why not implement the same thing like this:

Object.createChild = function(obj, constructor) {
    function F() { this.parent = Object(obj); }
    F.prototype = obj;
    var out = new F();
    if (typeof constructor == 'function') {
        constructor.apply(out);
    }
    return out;
};

Then use

var a = Object.createChild(null, function () { /*...*/ });
var b = Object.createChild(a, function () { /*...*/ });

with the same results as above. Bonus: You can omit the constructor argument, like this:

var c = Object.createChild(anything);

Second, I don't know if there's any use for true prototypal inheritance, as you call it. In real life, I'm pretty sure the constructor function is particularly tailored to the object that's about to be extended (a). Thus, you're gonna end up calling

var x = f.from(a);
var y = f.from(a);

with the very same f-a combination over and over again. And yes, you save some bytes of memory as compared to a class-driven approach, but honestly, who cares?

Still, the whole thing is a really good idea in theory.

user123444555621
  • 148,182
  • 27
  • 114
  • 126
  • You make some good points. I can agree that prototypal inheritance is something that is more useful in theory than practice (in fact, I never really need inheritance in my projects, so I just use the module pattern), but my idea was basically a way to try and make prototypal inheritance more useful. You get the benefits of private instance variables as well as inheritance capabilities without too much "hacking" the language (like all those libraries with pseudo-classes). – Nick Jul 31 '10 at 21:22
  • Personally, I like the idea of creating "classes" from objects. Your idea is good to do that, with slight modifications. Put `constructor` inside `F`'s body, and return `F`. This is somewhat similar to [ExtJS's `extend` method](http://www.sencha.com/deploy/dev/docs/?class=Ext&member=extend), which I find very useful. – user123444555621 Aug 01 '10 at 06:37