I am looking to learn about class inheritance and how it may be emulated without using the class keyword. It's just an academic/learning exercise.
Does the following method work for doing class inheritance, or am I missing some core concepts?
function Person(name, age) {
let person = Object.create(Person.prototype);
person.name = name;
person.age = age;
return person;
}
Person.prototype.get_birth_year = function() {
console.log(new Date().getFullYear() - this.age);
}
function Kid(name, age, dad, mom) {
let kid = Object.create(Person.prototype);
kid.dad = dad;
kid.mom = mom;
for (let key in Kid.prototype) {
kid[key] = Kid.prototype[key];
}
return kid;
}
Kid.prototype.callMom = function() {
console.log(`MOM!!! (${this.mom})`);
}
function KidToy(name, age, dad, mom, toyname) {
let toy = Object.create(Kid.prototype);
toy.toyname = toyname;
for (let key in KidToy.prototype) {
toy[key] = KidToy.prototype[key];
}
return toy;
}
KidToy.prototype.findToy = function() {
console.log('Looking to find the', this.toyname);
}
let p = Person('Bob', 33);
p.get_birth_year();
let k = Kid('Billy', 12, 'Bob', 'Margot');
k.callMom();
let t = KidToy('Billy', 12, 'Bob', 'Margot', 'Potatohead');
t.findToy();
The only things I'm doing here to subclass are:
Or, am I missing some things?
Not quite. There are three approaches to prototypal inheritance in JS:
new keyword with a function that acts as a constructor, and assigning methods to that function's .prototype so they are inherited by the instances created by it.Object.create function to manually create objects that inherit from others.class, constructor and super syntax to write classical-style code in JS. JS classes can only be instantiated with the new keyword and support some unique features, though they still use prototypal inheritance underneath.When you call a function with the new keyword, it automatically creates an object that inherits from the function's .prototype and exposes it as this within the function body.
function Person(name, age) {
this.name = name;
this.age = age;
}
const john = new Person("John", 42);
You don't need to create the instance and return it, this is all done by the new keyword. Objects like john above will have access to all properties stored in Person.prototype that aren't shadowed by properties assigned to john itself.
Person.prototype is itself an object that was created automagically when the Person function was defined. It inherits from Object and it has one property, constructor, which points back to the Person function.
A few more corrections about your assumptions:
let kid = Object.create(Person.prototype);
With this code, kid does not inherit from Kid.prototype.
for (let key in Kid.prototype) {
kid[key] = Kid.prototype[key];
}
This code copies all the enumerable properties of Kid.prototype into kid at the time of creation. This means that if you ever add properties to Kid.prototype later, they won't be in kid. If kidactually inherited fromKid.prototype, methods could be added to Kid.prototype` even after a kid instance was created, and that instance would inherit those methods.
Instead, you probably wanted something like this:
function Kid(name, age, dad, mom) {
// invoke super constructor
Person.call(this, name, age);
this.dad = dad;
this.mom = mom;
}
// Kid extends Person
Kid.prototype = Object.create(Person.prototype);
Kid.prototype.constructor = Kid;
All kid instances created after this code will inherit not from the default Kid.prototype which itself inherits from Object, but from our custom-made Kid.prototype which inherits from Person.prototype.
As you can see, this isn't very intuitive or practical, which is why class syntax was added to the language.
Hopefully this gives you a push in the right direction. I personally rarely use inheritance in my own code, and instead adopt a more FP approach with closures and composition.