Understanding JavaScript Inheritance - javascript

Block = function (){
this.type = 'block';
if(arguments[0]) this.type = arguments[0];
this.location = {x: 0, y: 0};
function update(){
}
}
Empty = function(location){
this.prototype = new Block;
this.type = 'empty';
this.location = location;
}
I want to be able to call
var x = new Empty();
x.update();
But I get the error that x.update is not a function.

The prototype property is only useful on a function. In the Empty constructor you're setting it on each instance of Empty, so it basically does nothing.
A much better way to do inheritance with JavaScript than what you're trying to do is to set the prototype of the inheriting class to inherit from the prototype of the base class:
Empty.prototype = Object.create(Block.prototype);
... and in order to inherit the properties set in the base class constructor, you simply call it with the correct arguments in the inheriting class constructor:
Empty = function(location) {
Block.call(this, location); // call base class
// other code, specific to your inheriting class
}
Note that the Empty.prototype line should come after you define Empty:
Empty = function(location) {
Block.call(this, location); // call base class
// other code, specific to your inheriting class
}
Empty.prototype = Object.create(Block.prototype);
Finally, to make methods available to instances you can define them in the constructor for each instance:
Block = function() {
this.update = function() { };
}
... or on the prototype of the constructor (after you define the constructor, obviously):
Block.prototype.update = function() {};
I don't know your particular scenario, but it seems to me that your inheritance is slightly weird. Usually the base is the more generic type (with variable location) and the inheriting class specializes it. The base class would be Doctor (person who treats diseases) and the inheriting class would be Dentist (person who treats certain kinds of diseases).

You don't set the prototype of this inside the constructor (where it would point to the new object, which doesn't have a prototype property with any meaning), you set it on the constructor directly (Block in this case).
Also, update is hidden in your case. You need to assign it to this (not so great practice) or make it part of Block's prototype, which you should, otherwise there is no real point using delegation or inheritance here.
Your code should look more or less like...
var Block = function () {
this.type = 'block';
if (arguments[0]) this.type = arguments[0];
this.location = {
x: 0,
y: 0
};
};
Block.prototype.update = function () {
console.log("updating");
};
var Empty = function (location) {
this.type = 'empty';
this.location = location;
};
Empty.prototype = new Block;
var x = new Empty();
x.update();
jsFiddle.

Related

JS ECMA5: converting object to child class

Code example:
ClassA.js
var classB = require('./ClassB');
function ClassA() {
this.ID = undefined;
this.Type = undefined;
....
}
ClassA.prototype.init = function init(id){
this.ID = id;
this.get();
if (this.Type === 'C' && Object.getPrototypeOf(this) === ClassA.prototype) {
return new classB().init(this.ID);
}
}
ClassB.js
function ClassB() {
ClassA.call(this);
this.additionalProp = undefined;
}
ClassB.prototype = Object.create(ClassA.prototype);
ClassB.prototype.constructor = ClassB;
I have implemented two classes ClassA and ClassB.
ClassB is a child of CLassA and has some additional properties.
The prototype chain for ClassB is setup like this:
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
The information for the objects is retrieved from an API via an ID.
At the time of the API call I do not know if the object needs to be an instance of ClassA or ClassB. So I always start with an object of ClassA to call the API.
When the API returns a specific Type I want to convert my object from ClassA to more specific ClassB.
I tried to call the constructor of ClassB out of ClassA - this throws an error:
Uncaught TypeError: Object prototype may only be an Object or null: undefined
I don't think I should reference ClassB in ClassA at all, but it was worth a try...
Thank you for helping out a novice! :)
At the time of the API call I do not know if the object needs to be an instance of ClassA or ClassB. So I always start with an object of one class, then when the API returns a specific type I want to convert my object.
Just don't do that. Construct your object after the API returns, and construct it with the correct type in the first place.
I always start with an object of the class to call the API
This is the point where your design has gone wrong. The class should not be responsible for calling the API, it should be responsible only for representing the data and having methods act on the data. Don't start with an empty object that can somehow hydrate itself, have the constructor completely initialise the object from the data passed as parameters.
Put the API call in a separate function, outside of the class. Create a separate class to represent the API endpoint maybe. If absolutely necessary, make it a static method of your base class, it still can create new ClassA or new ClassB instances then.
In a comment you've mentioned this error:
Uncaught TypeError: Object prototype may only be an Object or null: undefined
You get that because ClassA.js and ClassB.js have a circular relationship: each tries to import something the other exports. That can be fine in some cases, but here you have code in ClassB.js trying to use ClassA.prototype in top-level code, and ClassA.js importing ClassB from ClassB.js. You end up with a placeholder for the ClassA import and the Object.create(ClassA.prototype) call doesn't work.
It's going to be much easier if you define both of them in the same file, thanks to function declaration hoisting.
I'd also modify init so that it always returns the instance, since you need to return a new object sometimes but not other times. So have it always return the instance simplifies the calling code.
Here's a minimal-changes example:
function ClassA() {
this.ID = undefined;
this.Type = undefined;
// ....
}
ClassA.prototype.get = function () {
// *** Just for debugging, ID 2 is `ClassB`, any other is `ClassA`
this.Type = this.ID === 2 ? "C" : "X";
};
ClassA.prototype.init = function init(id) {
this.ID = id;
this.get();
if (this.Type === "C" && Object.getPrototypeOf(this) === ClassA.prototype) {
return new ClassB().init(id); // *** Use `ClassB`, not `ClassA`
}
return this; // *** So the caller always gets an instance they can use
};
function ClassB() {
ClassA.call(this);
this.additionalProp = undefined;
}
ClassB.prototype = Object.create(ClassA.prototype);
ClassB.prototype.constructor = ClassB;
module.exports.ClassA = ClassA;
module.exports.ClassB = ClassB;
Then using it (just for example):
var both = require("./both");
var ClassA = both.ClassA;
var ClassB = both.ClassB;
var obj1 = new ClassA();
obj1 = obj1.init(1);
console.log(obj1 instanceof ClassA); // true
console.log(obj1 instanceof ClassB); // false
var obj2 = new ClassA();
obj2 = obj2.init(2);
console.log(obj2 instanceof ClassA); // true
console.log(obj2 instanceof ClassB); // true
That said, I think I'd refactor this. You've said that there's a separate init method because sometimes you want to use methods on the objects before you have an id. That makes me think ClassA (at least) is trying to do too much, both things that it can do when it doesn't know what it is (no id) and things it can do when it does. The instance returned by a constructor should be fully baked and ready to go. So probably better to split the parts of ClassA that don't need an id off into something else. That would also mean that ClassA didn't have to refer to ClassB, which isn't best practice.
I think I'd probably also split get off to be separate from the classes, and have it return the appropriate instance.
For example:
ClassA.js:
function ClassA(data) {
this.ID = data.id;
this.Type = data.type;
// ....
}
// ...other `ClassA` methods...
module.exports = ClassA;
ClassB.js:
var ClassA = require("./ClassA");
function ClassB(data) {
ClassA.call(this, data);
this.additionalProp = data.additionalProp;
}
ClassB.prototype = Object.create(ClassA.prototype);
ClassB.prototype.constructor = ClassB;
// ...other `ClassB` methods...
module.exports = ClassB;
get.js (or whatever):
var ClassA = require("./ClassA");
var ClassB = require("./ClassB");
function get(id) {
var data = /*...get from API...*/;
var cls = "additionalData" in data ? ClassB : ClassA;
return new cls(data);
}
That provides a much better separation of concerns.

Is it possible in javascript to inherit properties from another class - rather than an existing object?

For example in PHP if you wanted a class to inherit properties of another class you would reference the parent class
<?php
class BaseController {
// ....
}
class UserController extends BaseController {
// ....
}
However in javascript if you want a new class or object to inherit some properties from another class - it seems you need to assign an - already instantiated - object of the class you want to inherit from to your objects prototype.
e.g. If you want to create a brand new object and access the properties of an existing object:
var robot = {
active : "yes",
primeDirective : function() {
console.log("Must kill all humans!");
}
};
var bender = Object.create(robot);
bender.primeDirective(); => "Must kill all humans!"
or if you have an existing object, you can assign the existing object to prototype using __proto__
var robot = {
active : "yes",
primeDirective : function() {
console.log("Do a flip!");
}
};
var bender = {
name : "Bender Bending Rodriguez"
};
bender.__proto__ = robot;
bender.primeDirective(); => "Do a flip!"
both these methods require an already created object to inherit properties from, is it possible for a class definition to inherit from another class - similar to the extends functionality in PHP?
In ES5 JavaScript the correct way to derive a class is to use Object.create passing the base class's prototype, not an instance, and then to ensure that all functions are part of that prototype.
// a properly formed constructor function
function Robot(name) {
this.name = name;
}
// all functions belong on the prototype
Robot.prototype.primeDirective = function() {
...
}
// create derived class
function BendingUnit22(name) {
Robot.call(this, name); // invoke superclass constructor
}
// create and attach a new prototype object chained from the base class
BendingUnit22.prototype = Object.create(Robot.prototype);
// and re-attach the constructor
BendingUnit22.prototype.constructor = BendingUnit22;
// add new or overriding functions here
BendingUnit22.prototype.primeDirective = function() {
...
}
var bender = new BendingUnit22("Bender Bending Rodriguez");
You'll need to create a constructor (or an ES6 class) if you want to inherit a little less dynamically.
function Robot() {
this.active = true;
}
Robot.prototype.primeDirective = function() {
console.log("Must kill all humans!");
};
var bender = new Robot(); // Yey!
To create a new inheriting constructor:
function HumanoidRobot() {
Robot.apply(this, arguments);
this.legs = 2;
}
HumanoidRobot.prototype = Object.create(Robot.prototype);
HumanoidRobot.prototype.constructor = HumanoidRobot;
This process becomes a lot easier with ES6 classes, which hide all this ugliness from you!
class Robot {
constructor() {
this.active = true;
}
primeDirective() {
console.log("Must kill all humans!");
}
}
class HumanoidRobot extends Robot() {
constructor() {
super()
this.legs = 2;
}
}
No, there is no built in mode for extending classes in Javascript, because it is not a class based but prototype based language.
However, there are many frameworks that implement the 'extend' behaviour, for example in Prototype:
var robot = Class.extend({ ... });
var bender = robot.extend({ ... });
http://ejohn.org/blog/simple-javascript-inheritance/
But many other frameworks support the same, for example Underscore _.extend()
http://underscorejs.org/#extend
There are 3 kind of inheritance possible in JavaScript.
Pseudo Classical (Like the one you are looking for)
/**
* Create a new constructor function, whose prototype is the parent object's prototype.
* Set the child's prototype to the newly created constructor function.
**/
var extendObj = function (childObj, parentObj) {
var tmpObj = function () {}
tmpObj.prototype = parentObj.prototype;
childObj.prototype = new tmpObj();
childObj.prototype.constructor = childObj;
};
(https://jsfiddle.net/nikdtu/4wzuwhqw/)
Functional
(https://jsfiddle.net/nikdtu/eh7u4pxd/)
Prototypal (Object.create)
(https://jsfiddle.net/nikdtu/dnjkx8w1/)
Luckily I documented that during a project and captured those JSfiddles.
I hope You will find the required help from these.
I think this is what you are looking for
// 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 = new Person();
// 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

Javascript - Pack properties of parent and child in the same JSON string

I have a javascript scenario where I have created a base class and a derived class and wish to pack the total set of properties into a JSON-string with JSON.stringify().
When I use the equivalent to the code below I only get the child-object's properties when I run "toString()" on one of the DerivedClass instances:
function BaseClass() {
this.version = "0.0.0";
this.time = Date.now();
this.type = this.constructor.name;
}
BaseClass.prototype.BaseClassException = function(message) {
this.message = message;
}
BaseClass.prototype.toString = function() {
return JSON.stringify(this);
}
BaseClass.parse = function(jsonString) {
var json = JSON.parse(jsonString);
switch(json.type) {
case "DerivedClass1":
return new DerivedClass1();
case "DerivedClass2":
return new DerivedClass2();
default:
throw new BaseClassException("No compatible type found when parsing: " + jsonString);
}
function DerivedClass1(prop1, prop2) {
this.prop1 = prop1;
this.prop2 = prop2;
this.type = this.constructor.name;
}
DerivedClass1.prototype = new BaseClass();
DerivedClass1.prototype.constructor = DerivedClass1;
function DerivedClass2(prop3) {
this.prop3 = prop3;
}
DerivedClass2.prototype = new BaseClass();
DerivedClass2.prototype.constructor = DerivedClass2;
// Test
var dc1 = new DerivedClass1("A", "B");
console.log(dc1.toString()); // Returns JSON-string with properties of DerivedClass1, but not BaseClass
There will be several different derived classes. While I do know that js doesn't really support classes I would still like to pack all the properties from the base and child objects in the same JSON-string. The structure is necessary to correlate to the other nodes of the total system, ie all properties need to be present.
If anyone at the same time has the knowledge of nudging me in the correct direction to understand the link between the child object and parent object in order for me to better understand the "inheritance" part of js I'd be really thankful as well. I more used to strict oo-languages so I'd be happy to learn.
There are two things which I can readily suggest.
To invoke the base class constructor, you have to invoke it manually like this
function DerivedClass1(prop1, prop2) {
BaseClass.call(this);
this.prop1 = prop1;
this.prop2 = prop2;
this.type = this.constructor.name;
}
We invoke the parent constructor function, with the current object. The important thing to note here is that, we are setting the current context to the object of type DerivedClass1.
To actually do prototypal inheritance, you need to use the base class's prototype, not the object.
DerivedClass1.prototype = Object.create(BaseClass.prototype);
In your case, BaseClass's constructor doesn't depend on any arguments. So, doing DerivedClass1.prototype = new BaseClass(); will not make a big difference. But it is always better to depend only on the Parent constructor's prototype. Read more about using Object.create for inheritance, in this wonderful answer.

inherit prototype methods from other classes without overriding own prototype methods

Is there a better way of having a class inherit prototype methods from another class and still be able to define new prototype methods on the class that inherits than this:
var ParentConstructor = function(){
};
ParentConstructor.prototype = {
test: function () {
console.log("Child");
}
};
var ChildConstructor = function(){
ParentConstructor.call(this)
};
ChildConstructor.prototype = {
test2: "child proto"
};
var TempConstructor = function(){};
TempConstructor.prototype = ParentConstructor.prototype;
ChildConstructor.prototype = new TempConstructor();
ChildConstructor.prototype.constructor = ChildConstructor;
var child = new ChildConstructor();
child.test();
console.log(child.test2)
console.log(child, new ParentConstructor());
Which isn't working, because I lose my test2 prototype method / property when I add the inheritance from my ParentConstructor.
I've tried other ways to extend the prototype methods of a class with some prototype props form other classes but I have failed each time, because I couldn't find a way not to override the previous methods each time.
I have also tried the var Child = Object.create(Parent.Prototype), but when I define new props I lose the parent props.
Setting up inheritance should take place before you define new properties on the prototype of ChildConstructor. And when you define new prototype properties, you also shouldn't override the whole prototype property. Instead, you can simply add new properties, like you already did with the constructor property:
ChildConstructor.prototype = new ParentConstructor();
ChildConstructor.prototype.constructor = ChildConstructor;
ChildConstructor.prototype.test2 = "child proto";
The best example I can think of comes from:
http://robertnyman.com/2008/10/06/javascript-inheritance-how-and-why/
function Being() {
this.living = true;
this.breathes = function () {
return true;
};
}
function Robert() {
// Robert.prototype = new Being(); /* edit */
this.blogs = true;
this.getsBored = function () {
return "You betcha";
};
}
Robert.prototype = new Being();
Robert.prototype.newMethod = function() {
console.log('new method executed');
return this;
}
Note this example, has been updated, the first comment below is directed at the first code I had up, which contained the prototype inside the Robert method.

Trouble Defining a Class in JavaScript

I am trying to learn how to define a class in JavaScript. I found this link (http://www.phpied.com/3-ways-to-define-a-javascript-class/) but it doesn't seem to meet my needs. Essentially, I want to have a class that has properties and functions. When a class is initialized, I want to automatically call the init function. At this time, when I create a new Item using the following code, I get an error:
var item = new Item();
The error says: Object has no method 'init'.
My class definition looks like the following:
function Item() {
this.id = null;
this.name = "";
this.description = "";
this.init();
this.init = function () {
this.id = "54321";
};
}
What am I doing wrong? How do I create a constructor in JavaScript?
You're calling init() before you've defined it. Simply move the invocation below the definition:
function Item() {
this.id = null;
this.name = "";
this.description = "";
this.init = function () {
this.id = "54321";
};
this.init();
}
As you are instantiating the Item class, you could use the prototype of Item to take some of the logic out of the constructor. In the example below, Item is instantiated with all default properties from the prototype (including the init function)
function Item() {
this.init();
}
Item.prototype = {
id: null,
name: '',
description: '',
init: function () {
this.id = "54321";
}
};
There are many ways to manipulate the prototype - here it is set to an Object to set all properties at once, but it could have had each item added individually by using Item.prototype.someVariable = something style syntax.
One note on setting the prototype - any non-primitive data type (ie: not boolean, number or string) will be shared across all instances (useful for sharing functions like the init function) whereas primitives will be per instance.
You are calling the init method before it's defined. Only FunctionDeclarations in JavaScript are hoisted. Not FunctionExpressions assigned to a variable. Remember, only declarations are hoisted. Not definitions.

Categories

Resources