10

Note:

As the answers tell, the code proposed in the question does NOT really achieve inheritance(otherwise it becomes an answer rather than a question .. ) due to some issues described in the question and in my comments. It works as expected as a fake of inheritance(and not even prototypal).


  • Summary

    In short, make it as similar as we are writing a general OO language rather than javascript, but keep the inheritance be correct.

  • The story

    Object.create is a good way to achieve prototypal inheritance, but it's a bit confusing to a typed brain and new fans.

    There are various ways that we can write javascript code more like we are writing other OO languages with the pseudo-classical pattern. As it's pseudo-classical, we must deal with the underlying prototypal inheritance of javascript correctly.

    What I want to find is an approach that the pseudo-classical inheritance can be achieved right on the class declaration. The code for demonstration is put at the rear of the post, it works as expected, however, there are some annoying things:

    1. I cannot get rid of return in the class declaration or the inheritance won't work.

    2. I have no way except pass this in the class declaration to make the returning closure know what is this.

    3. I also want to get rid of function (instance, _super) {, but not yet have a good idea.

    4. The static(own properties) of a class are not inherited.

    A solution would be more of some syntactic sugar than the existing frameworks, a good pattern is applicable.


The _extends function:

function _extends(baseType) {
    return function (definition) {
        var caller=arguments.callee.caller;
        var instance=this;

        if(!(instance instanceof baseType)) {
            (caller.prototype=new baseType()).constructor=caller;
            instance=new caller();
        }

        var _super=function () {
            baseType.apply(instance, arguments);
        };

        definition(instance, _super);
        return instance;
    };
}

The Abc class:

function Abc(key, value) {
    return _extends(Object).call(this, function (instance, _super) {
        instance.What=function () {
            alert('What');
        };

        instance.getValue=function () {
            return 333+Number(value);
        };

        instance.Value=instance.getValue();
        instance.Key=key;
    });
}

The Xyz class:

function Xyz(key, value) {
    return _extends(Abc).call(this, function (instance, _super) {
        _super(key, value);

        instance.That=function () {
            alert('That');
        };
    });
}

Example code:

var x=new Xyz('x', '123');
alert([x.Key, x.Value].join(': ')+'; isAbc: '+(x instanceof Abc));

var y=new Xyz('y', '456');
alert([y.Key, y.Value].join(': ')+'; isAbc: '+(y instanceof Abc));

var it=new Abc('it', '789');
alert([it.Key, it.Value].join(': ')+'; isAbc: '+(it instanceof Abc));
alert([it.Key, it.Value].join(': ')+'; isXyz: '+(it instanceof Xyz));

x.What();
y.That();

it.What();
it.That(); // will throw; it is not Xyz and does not have That method
Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
Ken Kin
  • 4,503
  • 3
  • 38
  • 76
  • 2
    Hi Ken, what is it you're trying to accomplish? You don't want to `Xyz.prototype.somefunction = ...` instead have everything in the constructor body so it looks more like a Java class definition? Maybe typescript and it's tooling works for you, besides solving the syntax confusion of JavaScript you have typechecks that show up in your IDE while coding. A better solution in my opinion would be to get used to the syntax because it may be a while before all JS engines implement ECMA 6 class definition (at the moment I think it's only a suggestion). – HMR Sep 12 '13 at 04:55
  • @HMR: Yes, make it more like a type-based langage by some syntactic means. I don't know TypeScript before, sounds insteresting. – Ken Kin Sep 12 '13 at 05:09
  • There is a link in my answer to the playground, on the home page is a video that is worth watching. The JS produced code isn't closure friendly but Michael Bolin has written compilers for typescript that will produce closure friendly JS. The main reason why I haven't used it much is because I'm waiting for Netbeans to support it properly. – HMR Sep 12 '13 at 05:46
  • #4 [is quite impossible](http://stackoverflow.com/questions/13878432/javascript-static-inheritance/13879678#13879678), you can only make it non-dynamic (by simply copying a snapshot of properties onto the other constructor) – Bergi Sep 19 '13 at 03:02
  • @Bergi: Well .. I so guess that we can do this in a relatively conventional way such as `(Abc.staticInitializer=function () { Abc.blah=function () { return 'blah'; }; })();` – Ken Kin Sep 19 '13 at 03:47
  • http://www.crockford.com/javascript/inheritance.html – krishnakumarp Sep 20 '13 at 04:46
  • @AaditMShah: I believe your point is correct. I probably would not write an answer which contains plenty of knowledge about the inheritance in javascript but *just as a solution*, so I'm going to accept your answer. Thank you very much, and I'll undetele my answer as a resource after I improved it. – Ken Kin Oct 22 '13 at 15:33
  • Lol. Technically the first sentence in my answer is just "No." I get your drift though. However I don't like deleting information. As far as possible I only add content to my articles. Removing the first line of my answer would invalidate some comments so I'll just let it be there. If you wish I could add some more content to it though. – Aadit M Shah Oct 22 '13 at 15:50

5 Answers5

23

No. No. No. This won't do. You're doing inheritance in JavaScript all wrong. Your code gives me migraines.

Creating a Pseudo-Classical Inheritance Pattern in JavaScript

If you want something similar to classes in JavaScript then there are a lot of libraries out there which provide it to you. For example using augment you could restructure your code as follows:

var augment = require("augment");

var ABC = augment(Object, function () {
    this.constructor = function (key, value) {
        this.key = key;
        this.value = value;
    };

    this.what = function () {
        alert("what");
    };
});

var XYZ = augment(ABC, function (base) {
    this.constructor = function (key, value) {
        base.constructor.call(this, key, value);
    };

    this.that = function () {
        alert("that");
    };
});

I don't know about you but to me this looks a lot like classical inheritance in C++ or Java. If this solves your problem, great! If is doesn't then continue reading.

Prototype-Class Isomorphism

In a lot of ways prototypes are similar to classes. In fact prototypes and classes are so similar that we can use prototypes to model classes. First let's take a look at how prototypal inheritance really works:

The above picture was taken from the following answer. I suggest you read it carefully. The diagram shows us:

  1. Every constructor has a property called prototype which points to the prototype object of the constructor function.
  2. Every prototype has a property called constructor which points to the constructor function of the prototype object.
  3. We create an instance from a constructor function. However the instance actually inherits from the prototype, not the constructor.

This is very useful information. Traditionally we've always created a constructor function first and then we've set its prototype properties. However this information shows us that we may create a prototype object first and then define the constructor property on it instead.

For example, traditionally we may write:

function ABC(key, value) {
    this.key = key;
    this.value = value;
}

ABC.prototype.what = function() {
    alert("what");
};

However using our newfound knowledge we may write the same thing as:

var ABC = CLASS({
    constructor: function (key, value) {
        this.key = key;
        this.value = value;
    },
    what: function () {
        alert("what");
    }
});

function CLASS(prototype) {
    var constructor = prototype.constructor;
    constructor.prototype = prototype;
    return constructor;
}

As you can see encapsulation is easy to achieve in JavaScript. All you need to do is think sideways. Inheritance however is a different issue. You need to do a little more work to achieve inheritance.

Inheritance and Super

Take a look at how augment achieves inheritance:

function augment(body) {
    var base = typeof this === "function" ? this.prototype : this;
    var prototype = Object.create(base);
    body.apply(prototype, arrayFrom(arguments, 1).concat(base));
    if (!ownPropertyOf(prototype, "constructor")) return prototype;
    var constructor = prototype.constructor;
    constructor.prototype = prototype;
    return constructor;
}

Notice that the last three lines are the same as that of CLASS from the previous section:

function CLASS(prototype) {
    var constructor = prototype.constructor;
    constructor.prototype = prototype;
    return constructor;
}

This tells us that once we have a prototype object all we need to do is get its constructor property and return it.

The first three lines of augment are used to:

  1. Get the base class prototype.
  2. Create a derived class prototype using Object.create.
  3. Populate the derived class prototype with the specified properties.

That's all that there is to inheritance in JavaScript. If you want to create your own classical inheritance pattern then you should be thinking along the same lines.

Embracing True Prototypal Inheritance

Every JavaScript programmer worth their salt will tell you that prototypal inheritance is better than classical inheritance. Nevertheless newbies who come from a language with classical inheritance always try to implement classical inheritance on top of prototypal inheritance, and they usually fail.

They fail not because it's not possible to implement classical inheritance on top of prototypal inheritance but because to implement classical inheritance on top of prototypal inheritance you first need to understand how true prototypal inheritance works.

However once you understand true prototypal inheritance you'll never want to go back to classical inheritance. I too tried to implement classical inheritance on top of prototypal inheritance as a newbie. Now that I understand how true prototypal inheritance works however I write code like this:

function extend(self, body) {
    var base = typeof self === "function" ? self.prototype : self;
    var prototype = Object.create(base, {new: {value: create}});
    return body.call(prototype, base), prototype;

    function create() {
        var self = Object.create(prototype);
        return prototype.hasOwnProperty("constructor") &&
            prototype.constructor.apply(self, arguments), self;
    }
}

The above extend function is very similar to augment. However instead of returning the constructor function it returns the prototype object. This is actually a very neat trick which allows static properties to be inherited. You can create a class using extend as follows:

var Abc = extend(Object, function () {
    this.constructor = function (key, value) {
        this.value = 333 + Number(value);
        this.key = key;
    };

    this.what = function () {
        alert("what");
    };
});

Inheritance is just as simple:

var Xyz = extend(Abc, function (base) {
    this.constructor = function (key, value) {
        base.constructor.call(this, key, value);
    };

    this.that = function () {
        alert("that");
    };
});

Remember however that extend does not return the constructor function. It returns the prototype object. This means that you can't use the new keyword to create an instance of the class. Instead you need to use new as a method, as follows:

var x = Xyz.new("x", "123");
var y = Xyz.new("y", "456");
var it = Abc.new("it", "789");

This is actually a good thing. The new keyword is considered harmful and I strongly recommend you to stop using it. For example it's not possible to use apply with the new keyword. However it is possible to use apply with the new method as follows:

var it = Abc.new.apply(null, ["it", "789"]);

Since Abc and Xyz are not constructor functions we can't use instanceof to test whether an object is an instance of Abc or Xyz. However that's not a problem because JavaScript has a method called isPrototypeOf which tests whether an object is a prototype of another object:

alert(x.key + ": " + x.value + "; isAbc: " + Abc.isPrototypeOf(x));
alert(y.key + ": " + y.value + "; isAbc: " + Abc.isPrototypeOf(y));

alert(it.key + ": " + it.value + "; isAbc: " + Abc.isPrototypeOf(it));
alert(it.key + ": " + it.value + "; isXyz: " + Xyz.isPrototypeOf(it));

In fact isPrototypeOf is more powerful than instanceof because it allows us to test whether one class extends another class:

alert(Abc.isPrototypeOf(Xyz)); // true

Besides this minor change everything else works just like it did before:

x.what();
y.that();

it.what();
it.that(); // will throw; it is not Xyz and does not have that method

See the demo for yourself: http://jsfiddle.net/Jee96/

What else does true prototypal inheritance offer? One of the biggest advantages of true prototypal inheritance is that there's no distinction between normal properties and static properties allowing you to write code like this:

var Xyz = extend(Abc, function (base) {
    this.empty = this.new();

    this.constructor = function (key, value) {
        base.constructor.call(this, key, value);
    };

    this.that = function () {
        alert("that");
    };
});

Notice that we can create instances of the class from within the class itself by calling this.new. If this.constructor is not yet defined then it returns a new uninitialized instance. Otherwise it returns a new initialized instance.

In addition because Xyz is the prototype object we can access Xyz.empty directly (i.e. empty is a static property of Xyz). This also means that static properties are automatically inherited and are no different from normal properties.

Finally, because the class is accessible from within the class definition as this, you can created nested classes which inherit from the class which they are nested within by using extend as follows:

var ClassA = extend(Object, function () {
    var ClassB = extend(this, function () {
        // class definition
    });

    // rest of the class definition

    alert(this.isPrototypeOf(ClassB)); // true
});

See the demo for yourself: http://jsfiddle.net/Jee96/1/

Community
  • 1
  • 1
Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
  • 1
    There's a known issue that the properties like `instance.What` in fact are not inherited from the prototype and each instance has its own property, which can be solved by adding an argument and passing the prototype to the initialize function such as `function (instance, prototype, _super)`; but that approach doesn't work with a closure captures the arguments of the constructor. I'm still reading your answer, and sorry for giving you migraines .. – Ken Kin Sep 19 '13 at 02:53
  • `augment` seems a good solution but I'm wondering how to declare an instance of type which is being declared, such as declaring `Xyz.Empty=new Xyz();` inside while `Xyz` is not yet declared, using `this.constructor` to do that may reduce the readability .. – Ken Kin Oct 17 '13 at 02:22
  • Another problem with `augment` that I cannot see a solution is to declare nesting classes which derive from the declaring class. – Ken Kin Oct 17 '13 at 03:35
  • @KenKin I edited my answer. It now features an `extend` function which I believe solves all your problems. Take a look at the __"Embracing True Prototypal Inheritance"__ section. – Aadit M Shah Oct 17 '13 at 11:05
  • @AaditMShah: Thanks for the update, but I cannot upvote twice :-). However, I don't really like your static properties, which are just prototype properties (and accessing them on the instances might trap some people into trying to assign them on the instances). I had more thought of something like [in this answer of mine](http://stackoverflow.com/a/11243549/1048572) where there are two separate (cross-linked) prototype chains for the `Class` objects and the instances. – Bergi Oct 17 '13 at 22:40
  • @Bergi Static properties should really not be accessed from instances. Yes it is possible to access static properties from instances but if you just abide by the rules (i.e. never assign to properties that don't belong to you) you shouldn't have a problem. Just last year I too wanted to [implement inheritable static properties](https://github.com/aaditmshah/clockwork) and my solution was very similar to yours. There are two prototype chains: the class prototype chain and the instance prototype chain. It wasn't a very good solution but the result looked a lot like classes in languages like Java – Aadit M Shah Oct 18 '13 at 00:23
3

There's an exhaustive tutorial on how to do what you're after.

oop-concepts

pseudo-classical-pattern

all-one-constructor-pattern

Asken
  • 7,679
  • 10
  • 45
  • 77
2

I know this doesn't answer your question because as far as I know there is no good way to put everything in the function constructor and have it use prototype.

As I've commented; if you have trouble with the JavaScript syntax then typescript could be a good alternative.

Here is a helper function that I use for inheritance and overriding (calling super) using JavaScript (without Object.create)

var goog={};//inherits from closure library base
  //http://docs.closure-library.googlecode.com/git/closure_goog_base.js.source.html#line1466
  // with modifications for _super
goog.inherits = function(childCtor, parentCtor) {
  function tempCtor() {};
  tempCtor.prototype = parentCtor.prototype;
  childCtor.prototype = new tempCtor();
  childCtor.prototype.constructor = childCtor;
  // modified _super
  childCtor.prototype._super = parentCtor.prototype;
};

// Parent class dev
var Parent = function(){};
Parent.prototype.sayHi=function(){
  console.log("hi from Parent");
}
// end class
// Child class dev
var Child = function(){}
goog.inherits(Child,Parent);
Child.prototype.sayHi=function(){
  //_super only works on parent.prototype
  //this is where functions are usually defined
  //have to add this. to call _super as well
  this._super.sayHi();
  console.log("hi from Child");
}
// end Child

//code to test
var c = new Child();
c.sayHi();//hi from Parent and hi from Child

Even if you find a way to write helper functions and make JS constructor functions look like Java classes you have to understand prototype.

Community
  • 1
  • 1
HMR
  • 37,593
  • 24
  • 91
  • 160
-2

You can always try jOOP, though it does require jQuery.

https://github.com/KodingSykosis/jOOP

var myClass = $.cls({
    main: function() {
        $('body').append('My App is loaded <br/>');
    }
});

var mySecondClass = $.cls({
    main: function() {
        this._super();
        $('body').append('My Second App is loaded <br/>');
    }
}, myClass);

var app = new mySecondClass();

http://jsfiddle.net/kodingsykosis/PrQWu/

-2

I believe you are looking for more functionality than this, but if you want to just inherit a bunch of methods from another class you could do this

http://cdpn.io/Jqhpc

var Parent = function Parent () {
  this.fname = 'Bob';
  this.lname = 'Jones';
};
Parent.prototype.getFname = function getFname () {
  return this.fname;
};
Parent.prototype.getLname = function getLname () {
  return this.lname;
};

var Child = function Child () {
  this.fname = 'Jim';
};
Child.prototype = Parent.prototype;

var child = new Child();
document.write(child.getFname()); //=> Jim
Joseph Spens
  • 668
  • 7
  • 12
  • 2
    Child and Parent shouldn't share the same prototype. `Child.prototype.getFname = null; var p = new Parent(); p.getFname() // error` Instead you should do `Child.prototype = Object.create(Parent.prototype);` – Tibos Oct 18 '13 at 13:59
  • Nice catch... my point simply was you could easily inherit methods from other objects. – Joseph Spens Oct 18 '13 at 16:31