How to achieve pseudo-classical inheritance directly in a class declaration?

Note:

As the answers say, the code suggested in the question does NOT really inherit (otherwise it becomes the answer, not the question ..) due to some problems described in the question and in my Comment. It works as an expected fake inheritance (and not even a prototype).




  • Summary

    In short, make it look like we are writing a common OO language, not javascript, but keep the inheritance correct.

  • Story

    Object.create is a good way to achieve prototypal inheritance, but it's a bit confused about typing and new .

    There are various ways to write javascript code, as we write other OO languages ​​with a pseudo-classic template. Since it is pseudo- classical, we must properly relate to the basic prototype inheritance of javascript.

    What I want to find is the approach according to which pseudo-classical inheritance can be achieved directly in the class declaration . The code for the demonstration is placed at the back of the message, it works as expected, but there are some unpleasant things:

    • I cannot get rid of return in the class declaration or inheritance will not work.

    • I have no way, except for pass this in the class declaration, to return a return message that there is this .

    • I also want to get rid of function (instance, _super) { , but I don't have a good idea yet.

    • Static (own properties) of a class are not inherited.

    The solution will be more syntactic sugar than the existing framework, apply a good template.




_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; }; } 

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; }); } 

Xyz Class:

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

Code example:

 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 
+11
javascript inheritance types prototype
Sep 12 '13 at 1:13
source share
5 answers

No. No. It will not happen. You are doing JavaScript inheritance all wrong. Your code gives me a migraine.

Creating a pseudo-classic inheritance pattern in JavaScript

If you need something similar to classes in JavaScript, then there are many libraries that provide this for you. For example, using augment , you can rebuild 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 do not know about you, but for me it is very similar to the classic inheritance in C ++ or Java. If this solves your problem, great! If not, continue reading.

Class prototype isomorphism

In many 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 see how prototype inheritance works :

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

  • Each constructor has a prototype property that points to a prototype object of the constructor function.
  • Each prototype has a constructor property that points to the constructor function of the prototype object.
  • We create an instance from the constructor function. However, the instance actually inherits from prototype , not the constructor.

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

For example, traditionally we can write:

 function ABC(key, value) { this.key = key; this.value = value; } ABC.prototype.what = function() { alert("what"); }; 

However, using our newfound knowledge, we can 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 easily achieved in JavaScript. All you have to do is think sideways. Inheritance, however, is another problem. You need to do a little more work to achieve inheritance.

Inheritance and super

See how augment reaches 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; } 

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

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

This tells us that when we have a prototype object, we only need to get its constructor property and return it.

The first three lines of the supplement are used for:

  • Get a prototype base class.
  • Create a prototype of the derived class using Object.create .
  • Fill out the prototype of the derived class with the specified properties.

That's all there is to JavaScript inheritance. If you want to create your own classic inheritance pattern, you should think along the same lines.

True Prototypal Inheritance Coverage

Every JavaScript developer who deserves his salt will tell you that prototypal inheritance is better than classical inheritance.Nevertheless , beginners who come from a language with classic inheritance always try to implement classical inheritance on top of prototypal inheritance, and usually they fail.

They fail not because it is not possible to implement classical inheritance on top of prototype inheritance, but because to implement classical inheritance on top of prototype inheritance you need to first understand how true prototype inheritance works .

However, once you understand true prototype inheritance, you will never want to return to classical inheritance. I also tried before to implement classical inheritance from above prototype inheritance as a beginner. Now that I understand how true prototype inheritance works, I write the code as follows:

 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 a constructor function, it returns a prototype object. This is actually a very neat trick that allows you to inherit static properties. 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 a constructor function. It returns a prototype object. This means that you cannot use the new keyword to instantiate a 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 good. The new keyword is considered malicious , and I highly recommend that you stop using it . For example, it is impossible to use apply with the new keyword . However, you can 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 cannot use instanceof to check if an object is an instance of Abc or Xyz . However, this is not a problem because JavaScript has a method called isPrototypeOf that checks if the 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 more powerful than instanceof , because it allows us to check whether one class extends to another class:

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

Apart from this minor change, everything else works the same as before:

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

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

What else does true prototype inheritance offer? One of the biggest advantages of true prototype inheritance is that there is no difference between normal properties and static properties that allow 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"); }; }); 

Note that we can instantiate a class from the class itself by calling this.new . If this.constructor is not yet defined, it returns a new uninitialized instance. Otherwise, it returns a new initialized instance.

In addition, since Xyz is a prototype object, we can directly access Xyz.empty (i.e. empty is a static property of Xyz ). It also means that static properties are automatically inherited and are no different from ordinary properties.

Finally, since the class is accessible from the class definition as this , you can create nested classes that inherit from the class in which they are nested 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 a demo for yourself: http://jsfiddle.net/Jee96/1/

+22
Sep 19 '13 at 2:27
source share

Here is a comprehensive tutorial on how to do what you need.

oop-concepts

pseudo-classical-pattern

all-one-constructor-pattern

+3
Sep 21 '13 at 3:50
source share

I know that this does not answer your question, because, as far as I know, there is no good way to put everything in a function constructor and use a prototype.

As I commented; if you are having problems with JavaScript syntax then typescript might be a good alternative.

Here is a helper function that I use to inherit and override (call 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 the prototype .

+2
Sep 12 '13 at 5:43 on
source share

You can always try jOOP, although this requires 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
Sep 20 '13 at 21:45
source share

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

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 
-2
Oct. 15 '13 at 17:39
source share



All Articles