i've got some problems with javascript prototype inheritance when this inheritance is stretched between multiple objects: surely i'm doing something wrong but for the moment i'm failing to understand what.
The whole Prototype's inheritance system seems to lose available methods when extending a prototype that already extends a prototype.
An example:
consider the following Objects
object A
prototype with extended function ab
prototype with extended function cd
object B
extends A
object C
extends B
prototype with extended function ef
object D
extends C
here an example of these Objects, as i have defined them:
Object A
function A () {
this.someproperty = someValue;
}
A.prototype.ab = function () {
// does something
}
A.prototype.cd = function () {
// does something
}
Object B
function B () {
A.call(this);
this.someOtherProperty = someValue;
}
B.prototype = A.prototype;
B.prototype.constructor = B;
Object C
function C () {
B.call(this);
}
C.prototype = B.prototype;
C.prototype.constructor = C;
C.prototype.ef = function () {
// does something
}
Object D
function D () {
C.call(this);
this.someOtherProperty = someValue;
}
D.prototype = C.prototype;
D.prototype.constructor = D;
given the example above, i am expecting that initializing a variable as "new D", such variable should have available the methods ab, cd, and ef, accessible with
variable.ab()
variable.cd()
variable.ef()
It seems instead that all of these are undefined.
Please consider that if i initialize "new B" instead:
variable.ab()
variable.cd()
are defined and working
am i doing something wrong or prototype inheritance cannot be over stretched over multiple objects?
Thank you!
B.prototype = A.prototype;
It's not inheritance, it's overwriting: A.prototype and B.prototype become the same object. It should be
B.prototype = Object.create(A.prototype);
Here is your code re-written with ES6 classes. #mbojko's answer is quite correct (so please accept that one rather than this as #mbojko is directly answering your question), however if you are going to use deep levels of class inheritance of this kind (and many would argue that you shouldn't - I tend to feel for some problems it is the right approach but you certainly need to be cautious) I would suggest using ES6 classes instead - clearer syntax and more guards against incorrect usage.
class A{
constructor (){
this.someproperty = 0;
}
ab(){
//...
return "called ab method"
}
cd(){
//...
}
}
class B extends A{
constructor(){
super();
this.someOtherProperty = 1;
}
}
class C extends B{
ef(){
//...
}
}
class D extends C{
constructor(){
super();
this.someOtherProperty = 2;
}
}
const d=new D();
console.log(d.ab());
Related
Take the following classes A, B and C and their instances a, b and c
class A {
constructor() {
this.method1 = function() {}
}
method2() {}
}
A.prototype.method3 = function() {};
class B extends A {
constructor() {
super();
}
}
class C {
constructor() {}
}
Object.assign(C.prototype, A.prototype);
const a = new A();
const b = new B();
const c = new C();
How come class B inherits method 2 but class C doesn't?
How can the following statements be both true ?
A.prototype.method2 !== undefined
c.method2 === undefined
In case you are wondering, c.method3 !== undefined, so there is some fundamental difference between the two that I cannot grasp.
You can fiddle around with it here https://jsfiddle.net/pdn64bv2/
Update
I thought it would be useful to share how to achieve the effect I was expecting Object.assign(C.prototype, A.prototype) to produce.
Object.getOwnPropertyNames(A.prototype).forEach(name => {
if (name !== "constructor") C.prototype[name] = A.prototype[name];
});
This copies the prototype of A to C, including non-enumerable properties.
Note that it does not copy the hole prototype chain the way Reflect.setPrototypeOf(C.prototype, A.prototype) would, which may be interesting for object composition.
There are two things conspiring to have this effect:
1) Object.assign will only copy the enumerable own properties of an object. Any non-enumerable properties will not be copied
2) When you use the class keyword, any methods you define are not enumerable. This is different from when you assign something to an object with A.prototype.method3 = function() {}, where the property will be enumerable.
So since A.prototype.method2 is not enumerable, it does not get put onto C.prototype when you do Object.assign(C.prototype, A.prototype);
Another way to see that class methods aren't enumerable is to use Object.keys. The ones defined with the class keyword are not there:
class A {
constructor() {
this.method1 = function() {}
}
method2() {}
}
A.prototype.method3 = function() {};
console.log(Object.keys(A.prototype));
console.log(A.prototype.method2); // it's there
console.log(Object.prototype.propertyIsEnumerable('method2')); // but it's not enumerable
A method from an inherited class should return the object type from who has inherited. In C++ this behaviour is easy to accomplish. But I don't know how to do it in javascript. I know it is wrong but I wrote like this.
class A {
someMethod() {
return new A();
}
}
class B extends A {
}
var b = new B();
b.someMethod() // should return an object of type B not A
in C++ this is easy to do
template <typename c>
struct a
{
a() : _value(NULL) {}
a(std::string v) : _v(v) {}
static c* from_string(std::string &v)
{
return new c(v);
}
private:
std::string _v;
};
struct b : public a<b>
{
b() : b<a>() {}
b(std::string &v) : node<b>(a) {}
};
How should this be implemented using javascript?
edit
This is not how inherits a class, is a particular pattern of inheriting and creating objects. There are several examples like Buffer.from, Buffer.alloc from Node.Js. But I would like to reproduce this from a base class.
A guy showed me that my issue could be solved using the following script:
class A {
method() { return this.constructor(); }
}
class B {}
var b = new B();
var b1 = b.method();
What I really would like to do is something like following.
class A {
static from() { return new this.constructor() }
};
class B extends A {};
a = A.from();
b = B.from();
a instanceof A // should be true
b instanceof B // should be true.
edit 2
I found something.
I found in typescript the same C++ behaviour can be archived as follows:
class a {
static from<t extends a>(c: { new(): t }) : t {
return new c();
}
};
class b extends a {
}
let b1 = a.from(b);
b1 instanceof b
b1 instanceof a
console.log(b1);
the es6 equivalent is:
class a {
static from(c) {
return new c();
}
}
;
class b extends a {
}
let b1 = a.from(b);
b1 instanceof b;
b1 instanceof a;
console.log(b1);
//# sourceMappingURL=index.js.map
As I've commented: a template class in C++ is actually a metaclass. It is used for constructing other classes out of it.
So we can apply this observation to JavaScript. I've played around and here's the closest thing I could get. First define a "template":
function TemplateA(cls) {
class A {
static from() {
return new cls();
};
foo() {
return -1;
};
};
return A;
};
Now define custom "extends":
function Extends(base, derived) {
// Update statics
// (these should not be overwritten)
var common = {name: 1, prototype: 1, length: 1};
var statics = Object.getOwnPropertyNames(base)
.filter(el => !common[el]);
statics.forEach(el => {
derived[el] = base[el];
});
// Update instance methods
var instanceMethods = Object.getOwnPropertyNames(base.prototype);
instanceMethods.forEach(el => {
derived.prototype[el] = base.prototype[el];
});
};
and finally usage:
class B {
test() { return 1; }
};
> Extends(TemplateA(B), B);
> var b = B.from();
> b instanceof B;
true
> var x = new B();
> x.foo();
-1
> x.test();
1
It seems to do what you want. This has some drawbacks though. It is not really an inheritance (the prototype is just updated). In particular B is not a subclass of A<B> (actually no such class even exists after Extends). On the other hand JavaScript's inheritance/prototyping is quite unique and very different from C++ and so we can't expect everything to work.
Side notes:
Is it safe? Probably. However it does require lots of discipline from a dev. For example it is too easy to overwrite something you didn't want to.
Would I use it in prod environment? Unlikely. Whatever you are trying to accomplish most likely can be achieved in some other, standard way. Treat the solution as an academic fun.
Finally: who told you that C++ way is the best way? Your swimming skills are not really useful when you are climbing, right? So I strongly suggest you rethink your entire architecture and try to do things differently.
Is it possible to call a baseclass constructor from a class?
class BaseCls {
}
class Cls extend BaseCls {
constructor(options){
super(options)
}
}
var instance = new Cls();
Now I want an instance of the baseclas. Something like this:
var baseInstance = new Cls.parent()
I know that I could just call new BaseCls(), but doing it the other way allows me to have only one import.
The superclass is the prototype of the subclass* (which is why superclass static methods are accessible on the subclass), so Object.getPrototypeOf will give you the superclass:
class BaseCls {
constructor() {
console.log("BaseCls");
}
}
class Cls extends BaseCls {
constructor(options){
super(options)
console.log("Cls");
}
}
var base = Object.getPrototypeOf(Cls);
var instance = new base();
You don't have to separate the statements, but if you want to combine them, you have to put () around the call to Object.getPrototypeOf (otherwise new tries to consume it):
var instance = new (Object.getPrototypeOf(Cls))();
And of course, if you wanted to do this generically from a reference to an instance of Cls, it would be:
var superInstance = new (Object.getPrototypeOf(instance.constructor))();
...provided instance doesn't have an own constructor property. Or the rather more convoluted:
var superInstance = new (Object.getPrototypeOf(Object.getPrototypeOf(instance).constructor))();
...if it may have its own constructor property.
* Yes, really. When you use B extends A, there are two parallel lines of inheritance set up: A.prototype is made the prototype of B.prototype, and A is made the prototype of B (whereas in ES5 and earlier, the prototype of a function was always Function.prototype).
Given:
class A {
}
class B extends A {
}
the inheritance looks like this:
B −−−−−−−−−−−−−−> A −−−−−−−−−−−−−−> Function.prototype
B.prototype −−−−> A.prototype −−−−> Object.prototype
class A {
}
class B extends A {
}
console.log(Object.getPrototypeOf(B) === A);
console.log(Object.getPrototypeOf(A) === Function.prototype);
console.log(Object.getPrototypeOf(B.prototype) === A.prototype);
console.log(Object.getPrototypeOf(A.prototype) === Object.prototype);
How would we polyfill es6 class methods into ES5?
I am reading a book and it says the following:
class Ninja {
constructor(name) {
this.name = name;
}
swingSword() {
return true;
}
}
is the same as
function Ninja(name) {
this.name = name;
}
Ninja.prototype.swingSword = function() {
return true;
};
I am just asking why are we adding the swingSword on the prototype and not inside the constructor function?
Because the function should be on the object and not on the prototype chain.
Am i right or wrong?
It should be on the prototype, methods are not per-instance data. Can't think of any language that implements it that way, the whole idea of classes is to have a whole class of objects that have the same set of methods.
If it was put it inside the constructor function, it would be a unique function per instance made with the constructor. e.g, 1000 objects == 1000 functions, per "method".
Adding the function to just the object would only work for a Ninja. To create a class that extends Ninja, for example Kunoichi, you would normally copy the Ninja prototype. Unfortunately, because swingSword is not in the prototype, your Kunoichi cannot swing swords.
You must add the function in prototype to allow the class to be extended.
If we add a method to the prototype, only one instance of that method exists in memory, and it’s shared between all objects created from the constructor.
If we add the swingSword method directly to the Ninja constructor function, then every object would have its own copy of that method, taking up more memory.
var $class = function ($superclass, config) {
// All classes have a superclass with the root
// of this $class hierarchy being Object.
var self = function (config) {
// Object.assign or $.extend or ...
config && Object.assign(this, config);
};
self.prototype = new $superclass(config);
return self;
};
var A = $class(Object, {
sayWhat: "Hello, I'm an A",
say: function () {
console.log(this.sayWhat);
}
});
var B = $class(A, {
sayWhat: "Hello, I'm a B"
});
var C = $class(B, {
say: function () {
console.log("C SAYS: " + this.sayWhat);
},
superSay: function () {
// how to call a superclass method
B.prototype.say.call(this);
}
});
var a = new A();
a.say(); // Hello, I'm an A
var b = new B();
b.say(); // Hello, I'm a B
var c = new C();
c.say(); // C SAYS: Hello, I'm a B
// create a "one-off" object
var d = new C({
sayWhat: "I'm special!",
say: function () {
console.log("hey!");
}
});
d.say(); // hey!
d.superSay(); // I'm special!
C.prototype.say.call(d); // C SAYS: I'm special!
I have a base class:
function Monster() {
this.health = 100;
}
Monster.prototype.growl = function() {
console.log("Grr!");
}
That I want to extend and create another class with:
function Monkey extends Monster() {
this.bananaCount = 5;
}
Monkey.prototype.eatBanana {
this.bananaCount--;
this.health++; //Accessing variable from parent class monster
this.growl(); //Accessing function from parent class monster
}
I've done quite a bit of research and there appears to be many convoluted solutions for doing this in JavaScript. What would be the simplest and most reliable way of accomplishing this in JS?
Updated below for ES6
March 2013 and ES5
This MDN document describes extending classes well:
https://developer.mozilla.org/en-US/docs/JavaScript/Introduction_to_Object-Oriented_JavaScript
In particular, here is now they handle it:
// define the Person Class
function Person() {}
Person.prototype.walk = function(){
alert ('I am walking!');
};
Person.prototype.sayHello = function(){
alert ('hello');
};
// define the Student class
function Student() {
// Call the parent constructor
Person.call(this);
}
// inherit Person
Student.prototype = Object.create(Person.prototype);
// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;
// replace the sayHello method
Student.prototype.sayHello = function(){
alert('hi, I am a student');
}
// add sayGoodBye method
Student.prototype.sayGoodBye = function(){
alert('goodBye');
}
var student1 = new Student();
student1.sayHello();
student1.walk();
student1.sayGoodBye();
// check inheritance
alert(student1 instanceof Person); // true
alert(student1 instanceof Student); // true
Note that Object.create() is unsupported in some older browsers, including IE8:
If you are in the position of needing to support these, the linked MDN document suggests using a polyfill, or the following approximation:
function createObject(proto) {
function ctor() { }
ctor.prototype = proto;
return new ctor();
}
Using this like Student.prototype = createObject(Person.prototype) is preferable to using new Person() in that it avoids calling the parent's constructor function when inheriting the prototype, and only calls the parent constructor when the inheritor's constructor is being called.
May 2017 and ES6
Thankfully, the JavaScript designers have heard our pleas for help and have adopted a more suitable way of approaching this issue.
MDN has another great example on ES6 class inheritance, but I'll show the exact same set of classes as above reproduced in ES6:
class Person {
sayHello() {
alert('hello');
}
walk() {
alert('I am walking!');
}
}
class Student extends Person {
sayGoodBye() {
alert('goodBye');
}
sayHello() {
alert('hi, I am a student');
}
}
var student1 = new Student();
student1.sayHello();
student1.walk();
student1.sayGoodBye();
// check inheritance
alert(student1 instanceof Person); // true
alert(student1 instanceof Student); // true
Clean and understandable, just like we all want. Keep in mind, that while ES6 is pretty common, it's not supported everywhere:
ES6 gives you now the opportunity to use class & extends keywords :
Then , your code will be :
You have a base class:
class Monster{
constructor(){
this.health = 100;
}
growl() {
console.log("Grr!");
}
}
That You want to extend and create another class with:
class Monkey extends Monster {
constructor(){
super(); //don't forget "super"
this.bananaCount = 5;
}
eatBanana() {
this.bananaCount--;
this.health++; //Accessing variable from parent class monster
this.growl(); //Accessing function from parent class monster
}
}
Try this:
Function.prototype.extends = function(parent) {
this.prototype = Object.create(parent.prototype);
};
Monkey.extends(Monster);
function Monkey() {
Monster.apply(this, arguments); // call super
}
Edit: I put a quick demo here http://jsbin.com/anekew/1/edit. Note that extends is a reserved word in JS and you may get warnings when linting your code, you can simply name it inherits, that's what I usually do.
With this helper in place and using an object props as only parameter, inheritance in JS becomes a bit simpler:
Function.prototype.inherits = function(parent) {
this.prototype = Object.create(parent.prototype);
};
function Monster(props) {
this.health = props.health || 100;
}
Monster.prototype = {
growl: function() {
return 'Grrrrr';
}
};
Monkey.inherits(Monster);
function Monkey() {
Monster.apply(this, arguments);
}
var monkey = new Monkey({ health: 200 });
console.log(monkey.health); //=> 200
console.log(monkey.growl()); //=> "Grrrr"
If you don't like the prototype approach, because it doesn't really behave in a nice OOP-way, you could try this:
var BaseClass = function()
{
this.some_var = "foobar";
/**
* #return string
*/
this.someMethod = function() {
return this.some_var;
}
};
var MyClass = new Class({ extends: BaseClass }, function()
{
/**
* #param string value
*/
this.__construct = function(value)
{
this.some_var = value;
}
})
Using lightweight library (2k minified): https://github.com/haroldiedema/joii
I can propose one variant, just have read in book, it seems the simplest:
function Parent() {
this.name = 'default name';
};
function Child() {
this.address = '11 street';
};
Child.prototype = new Parent(); // child class inherits from Parent
Child.prototype.constructor = Child; // constructor alignment
var a = new Child();
console.log(a.name); // "default name" trying to reach property of inherited class
This is an extension (excuse the pun) of elclanrs' solution to include detail on instance methods, as well as taking an extensible approach to that aspect of the question; I fully acknowledge that this is put together thanks to David Flanagan's "JavaScript: The Definitive Guide" (partially adjusted for this context). Note that this is clearly more verbose than other solutions, but would probably benefit in the long-term.
First we use David's simple "extend" function, which copies properties to a specified object:
function extend(o,p) {
for (var prop in p) {
o[prop] = p[prop];
}
return o;
}
Then we implement his Subclass definition utility:
function defineSubclass(superclass, // Constructor of our superclass
constructor, // Constructor of our new subclass
methods, // Instance methods
statics) { // Class properties
// Set up the prototype object of the subclass
constructor.prototype = Object.create(superclass.prototype);
constructor.prototype.constructor = constructor;
if (methods) extend(constructor.prototype, methods);
if (statics) extend(constructor, statics);
return constructor;
}
For the last bit of preparation we enhance our Function prototype with David's new jiggery-pokery:
Function.prototype.extend = function(constructor, methods, statics) {
return defineSubclass(this, constructor, methods, statics);
};
After defining our Monster class, we do the following (which is re-usable for any new Classes we want to extend/inherit):
var Monkey = Monster.extend(
// constructor
function Monkey() {
this.bananaCount = 5;
Monster.apply(this, arguments); // Superclass()
},
// methods added to prototype
{
eatBanana: function () {
this.bananaCount--;
this.health++;
this.growl();
}
}
);
For traditional extending you can simply write superclass as constructor function,
and then apply this constructor for your inherited class.
function AbstractClass() {
this.superclass_method = function(message) {
// do something
};
}
function Child() {
AbstractClass.apply(this);
// Now Child will have superclass_method()
}
Example on angularjs:
http://plnkr.co/edit/eFixlsgF3nJ1LeWUJKsd?p=preview
app.service('noisyThing',
['notify',function(notify){
this._constructor = function() {
this.scream = function(message) {
message = message + " by " + this.get_mouth();
notify(message);
console.log(message);
};
this.get_mouth = function(){
return 'abstract mouth';
}
}
}])
.service('cat',
['noisyThing', function(noisyThing){
noisyThing._constructor.apply(this)
this.meow = function() {
this.scream('meooooow');
}
this.get_mouth = function(){
return 'fluffy mouth';
}
}])
.service('bird',
['noisyThing', function(noisyThing){
noisyThing._constructor.apply(this)
this.twit = function() {
this.scream('fuuuuuuck');
}
}])
For Autodidacts:
function BaseClass(toBePrivate){
var morePrivates;
this.isNotPrivate = 'I know';
// add your stuff
}
var o = BaseClass.prototype;
// add your prototype stuff
o.stuff_is_never_private = 'whatever_except_getter_and_setter';
// MiddleClass extends BaseClass
function MiddleClass(toBePrivate){
BaseClass.call(this);
// add your stuff
var morePrivates;
this.isNotPrivate = 'I know';
}
var o = MiddleClass.prototype = Object.create(BaseClass.prototype);
MiddleClass.prototype.constructor = MiddleClass;
// add your prototype stuff
o.stuff_is_never_private = 'whatever_except_getter_and_setter';
// TopClass extends MiddleClass
function TopClass(toBePrivate){
MiddleClass.call(this);
// add your stuff
var morePrivates;
this.isNotPrivate = 'I know';
}
var o = TopClass.prototype = Object.create(MiddleClass.prototype);
TopClass.prototype.constructor = TopClass;
// add your prototype stuff
o.stuff_is_never_private = 'whatever_except_getter_and_setter';
// to be continued...
Create "instance" with getter and setter:
function doNotExtendMe(toBePrivate){
var morePrivates;
return {
// add getters, setters and any stuff you want
}
}
Summary:
There are multiple ways which can solve the problem of extending a constructor function with a prototype in Javascript. Which of these methods is the 'best' solution is opinion based. However, here are two frequently used methods in order to extend a constructor's function prototype.
ES 2015 Classes:
class Monster {
constructor(health) {
this.health = health
}
growl () {
console.log("Grr!");
}
}
class Monkey extends Monster {
constructor (health) {
super(health) // call super to execute the constructor function of Monster
this.bananaCount = 5;
}
}
const monkey = new Monkey(50);
console.log(typeof Monster);
console.log(monkey);
The above approach of using ES 2015 classes is nothing more than syntactic sugar over the prototypal inheritance pattern in javascript. Here the first log where we evaluate typeof Monster we can observe that this is function. This is because classes are just constructor functions under the hood. Nonetheless you may like this way of implementing prototypal inheritance and definitively should learn it. It is used in major frameworks such as ReactJS and Angular2+.
Factory function using Object.create():
function makeMonkey (bananaCount) {
// here we define the prototype
const Monster = {
health: 100,
growl: function() {
console.log("Grr!");}
}
const monkey = Object.create(Monster);
monkey.bananaCount = bananaCount;
return monkey;
}
const chimp = makeMonkey(30);
chimp.growl();
console.log(chimp.bananaCount);
This method uses the Object.create() method which takes an object which will be the prototype of the newly created object it returns. Therefore we first create the prototype object in this function and then call Object.create() which returns an empty object with the __proto__ property set to the Monster object. After this we can initialize all the properties of the object, in this example we assign the bananacount to the newly created object.
the absolutely minimal (and correct, unlike many of the answers above) version is:
function Monkey(param){
this.someProperty = param;
}
Monkey.prototype = Object.create(Monster.prototype);
Monkey.prototype.eatBanana = function(banana){ banana.eat() }
That's all. You can read here the longer explanation