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

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.

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.

Understanding Prototypal Inheritance methodologies in JS

I have read every article and example on the first page of Google and I still am having a hard to completely understanding how to properly implement Prototypal Inheritance in JavaScript. The biggest challenge I'm facing is I am seeing many different ways to implement the inheritance.
First I will start with what I want to achieve if this was C#:
C#
class Base {
public string UI { get; set; } // Using a string just for simplicity
}
class Book : Base {
public Book(string title) {
this.title = title;
}
private string title { get; set; } // Title of book
}
Then I can instantiate a Book and be able to access UI on each instance:
var myBook = new Book("East of Eden");
myBook.UI = "some string"; // This work.. all Book instances have THEIR OWN UI string
JavaScript
Now lets say I have a base object in JS that I want all other objects to inherit from:
function Base() {
this.UI = {}
}
I then want another object type to inherit this model like this:
function Book(title){
this.title = title;
}
Book.prototype = new Base();
// Sometimes I have seen this line instead... nothing seems to work at all when I use this though, so I don't understand whats happening here
//Book.prototype = Object.create(Base.prototype);
Book.prototype.constructor = Book;
Book.prototype.getTitle = function(){
return this.title;
}
var myBook = new Book("East of Eden");
var anotherBook = new Book("Grapes of Wrath");
console.log(myBook.getTitle()); // East of Eden
myBook.UI.isRead = true;
console.log(myBook.UI);
console.log(anotherBook.getTitle()); // Grapes of Wrath
anotherBook.UI.isHardcopy = true;
myBook.UI.isRead = false;
console.log(anotherBook.UI); // This UI object has isRead on it as well!!! NOOOO
So this doesn't really work because both instances are sharing the same UI object, but what I want is for them to have their OWN instance of the UI object.
Another method I have seen is to not use the 'new' keyword at all and only use Object.create() to get new objects. However, I am not sure how I would implement my Base class with some subclass like Book and then create multiple instances of Book, each with their own UI properties.
Could someone please give me an example of how to inherit a base class and create instances of that subclass with their own UI objects? Thanks
EDIT
So would the "simple" way of achieve what I want just be to do something like:
var Base = {
UI: {}
}
function Book(title){
_.extend(this, Base);
this.title = title;
}
var myBook = new Book("East of Eden");
myBook.UI.prop = 5; // This works now but doesn't utilize true inheritance at all!
Prototypes are linked and not copied. This means that when you did:
function Base(){
this.UI = {}
}
Book.prototype = new Base();
Book.prototype.constructor = Book;
The prototype of your Book constructor will be a new instance of Base. All your instances of Book will have the same prototype, the same instance of Base. Since it's this object which holds the UI property, all Book instances will fallback to the same object property.
Think that your Prototype will be:
var proto = {
UI : { }
}
All your Book instances will have access to this object:
var a = new Book('East of Eden');
var b = new Book('Grapes of Wrath');
a.UI.prop = 'prop'; //proto.UI.prop === 'prop'
b.UI.prop === 'prop'; //because it's also proto.UI.prop
If you actually define a property on Book instances, say on its constructor:
function Book(title){
this.title = title;
this.UI = { };
}
You'll see that they are different objects:
a.UI !== b.UI //true
a.UI.prop = 'prop';
b.UI.prop !== b.UI; //true
Calling the constructor is the most obvious way to also initialize the properties on their children:
function Book(title){
Base.call(this);
this.title = title;
}
Regarding the difference between new Base() and Object.create(Base.prototype).
new Base() will initialize the object and call the constructor, while Object.create(Base.prototype) will do basically the same, except it won't call the constructor. This means, that the prototype won't have the properties set on the constructor (UI).
The biggest challenge I'm facing is I am seeing many different ways to implement the inheritance.
There really is only one correct way.
Use Object.create to establish inheritance:
Child.prototype = Object.create(Parent.prototype, {
constructor: {
value: Child,
configurable: true,
}
});
Then apply the parent constructor in the child constructor to the child instance:
function Child() {
Parent.call(this);
}
This is basically what happens under the hood if you'd use ES2015's new class syntax.
See Benefits of using `Object.create` for inheritance for more details on Object.create and why you want to use it in this case.

reusable javascript objects, prototypes and scope

MyGlobalObject;
function TheFunctionICanUseRightAwaySingleForAllInstansesAndWithoutInstanse() {
function() {
alert('NO CONSTRUCTOR WAS CALLED');
}
};
The Long-named function must be callable from MyGlobalObject, which in turn must be available as a global (to window) variable in all times after script was loaded. It should support extensibility in accordance with latest standards.
I'm at architectural dilemma of how to built JS base for an application (almost 100% JS).
We need an object i.e. window.MyObject (like a module, like jQuery) so
It can be created with
VAR1
var MyGlobalObjConstructor = function(){
this.GlobalFunctionInObject = function(){
alert('called with MyGlobalObj.GlobalFunctionInObject()');
}
};
window.MyGlobalObj = new MyGlobalObjConstructor();
Is MyGlobalObj extensible? Can I create child objects, which will inherit current state of MyGlobalObj (extended functions/properties MyGlobalObj.NewFunc e.g.)? What is the main difference between using prototype (VAR3)?
By GlobaldFunction I mean single instance for all initialized/instantiated (possibly instantializable) instances..
Or with
VAR2
var MyGlobalObj = {
GlobalFunctionInObject: function...
GlobalFunctionInObject2: function...
};
MyGlobalObj.GlobalFunctionInObject();
// here I lose all hierarchy elements, no prototype,
// can I use GlobalFunctionInObject2 in GlobalFunctionInObject?
Or with
VAR3
var MyGlobalConstuctor = function(){} // already 'well-formed' object
MyGlobalConstuctor.prototype.GlobalFunctionInObject = function...
};
var MyGlobalObj = new MyGlobalConstuctor();
// so I'm sceptical to NEW, because I have ALREADY wrote my functions
// which I expect to be in memory, single instance of each of them,
// so creating MyObject2,3,4 with NEW MyGC() makes no sense to me.
// DO I REALLY HAVE TO USE "MyGlobalConstuctor.prototype." FOR EACH FUNCTION?!!!!
What's the difference defining MyGlobalObj as a function and as an object (result of func or VAR2)?
OR VAR4?
I see in Chrome Debugger both prototype and __proto__ special fields. I've read that that's OK, but why are they not saved in a single prototype?
So, what is the correct/optimal way to implement window.MyObject, so one could MyObject.MyFunction(); What are the differences (pro/contra) of variants 1 2 and 3?
Variation 1 - Mixin
function SomeType() {
var priv = "I'm private";
this.publ = "I'm public";
this.action = function() {
return priv + this.publ;
};
}
var obj = new SomeType();
With this method you are creating a new object every time you call new SomeType(), creating all its methods and adding all this method to the new object. Every time you create an object.
Pros
It looks like classical inheritance so it's easy to understand to Java-C#-C++-etc people.
It can have private variables per instance since you have one function closure per each object you create
It allows multiple inheritance, also known as Twitter-mixins or functional mixins
obj instanceof SomeType will return true
Cons
It consumes more memory as more objects you create because with each object you are creating a new closure and creating each of it's methods again.
Private properties are private, not protected, subtypes can't access them
No easy way to know if a object has some Type as superclass.
Inheritance
function SubType() {
SomeType.call(this);
this.newMethod = function() {
// can't access priv
return this.publ;
};
}
var child = new SubType();
child instanceof SomeType will return false there is no other way to know if child has SomeType methods than look if it has them one by one.
Variation 2 - Object literal with prototyping
var obj = {
publ: "I'm public",
_convention: "I'm public too, but please don't touch me!",
someMethod: function() {
return this.publ + this._convention;
}
};
In this case you are creating a single object. If you are going to need only one instance of this type it can be the best solution.
Pros
It's quick and easy to understand.
Performant
Cons
No privacy, every property is public.
Inheritance
You can inherit a object prototyping it.
var child = Object.create(obj);
child.otherMethod = function() {
return this._convention + this.publ;
};
If you are on a old browser you will need to garantee Object.create works:
if (!Object.create) {
Object.create = function(obj) {
function tmp() { }
tmp.prototype = obj;
return new tmp;
};
}
To know if a object is a prototype of another you can use
obj.isPrototypeOf(child); // true
Variation 3 - Constructor pattern
UPDATE: This is the pattern ES6 classes are sugar syntax of. If you use ES6 classes you are following this pattern under the hood.
class SomeType {
constructor() {
// REALLY important to declare every non-function property here
this.publ = "I'm public";
this._convention = "I'm public too, but please don't touch me!";
}
someMethod() {
return this.publ + this._convention;
}
}
class SubType extends SomeType {
constructor() {
super(/* parent constructor parameters here */);
this.otherValue = 'Hi';
}
otherMethod() {
return this._convention + this.publ + this.otherValue;
}
}
function SomeType() {
// REALLY important to declare every non-function property here
this.publ = "I'm public";
this._convention = "I'm public too, but please don't touch me!";
}
SomeType.prototype.someMethod = function() {
return this.publ + this._convention;
};
var obj = new SomeType();
You can re-assign the prototype insteadd of adding each method if you are not inheriting and remember to re-assign the constructor property:
SomeType.prototype = {
constructor: SomeType,
someMethod = function() {
return this.publ + this._convention;
}
};
Or use _.extend or $.extend if you have underscore or jquery in your page
_.extend(SomeType.prototype, {
someMethod = function() {
return this.publ + this._convention;
}
};
The new keyword under the hood simply does this:
function doNew(Constructor) {
var instance = Object.create(Constructor.prototype);
instance.constructor();
return instance;
}
var obj = doNew(SomeType);
What you have is a function than has no methods; it just has a prototype property with a list of functions, the new operator means to create a new object and use this function's prototype (Object.create) and constructor property as initializer.
Pros
Performant
Prototype chain will allow you to know if a object inherits from some type
Cons
Two-step inheritance
Inheritance
function SubType() {
// Step 1, exactly as Variation 1
// This inherits the non-function properties
SomeType.call(this);
this.otherValue = 'Hi';
}
// Step 2, this inherits the methods
SubType.prototype = Object.create(SomeType.prototype);
SubType.prototype.otherMethod = function() {
return this._convention + this.publ + this.otherValue;
};
var child = new SubType();
You may think it looks like a super-set of Variation 2... and you'll be right. It's like variation 2 but with a initializer function (the constructor);
child instanceof SubType and child instanceof SomeType will return both true
Curiosity: Under the hood instanceof operator does is
function isInstanceOf(obj, Type) {
return Type.prototype.isPrototypeOf(obj);
}
Variation 4 - Overwrite __proto__
When you do Object.create(obj) under the hood it does
function fakeCreate(obj) {
var child = {};
child.__proto__ = obj;
return child;
}
var child = fakeCreate(obj);
The __proto__ property modifies directly the object's hidden [Prototype] property. As this can break JavaScript behaviour, it's not standard. And the standard way is preferred (Object.create).
Pros
Quick and performant
Cons
Non-standard
Dangerous; you can't have a hashmap since the __proto__ key can change the object's prototype
Inheritance
var child = { __proto__: obj };
obj.isPrototypeOf(child); // true
Comment questions
1. var1: what happens in SomeType.call(this)? Is 'call' special function?
Oh, yes, functions are objects so they have methods, I will mention three: .call(), .apply() and .bind()
When you use .call() on a function, you can pass one extra argument, the context, the value of this inside the function, for example:
var obj = {
test: function(arg1, arg2) {
console.log(this);
console.log(arg1);
console.log(arg2);
}
};
// These two ways to invoke the function are equivalent
obj.test('hi', 'lol');
// If we call fn('hi', 'lol') it will receive "window" as "this" so we have to use call.
var fn = obj.test;
fn.call(obj, 'hi', 'lol');
So when we do SomeType.call(this) we are passing the object this to function SomeCall, as you remember this function will add methods to object this.
2. var3: With your "REALLY define properties" do you mean if I use them in functions? Is it a convention? Because getting this.newProperty without it being defined at the same level with other member functions is not a problem.
I mean any property your object will have that is not a function must be defined on the constructor, not on the prototype, otherwise you will face one of the more confusing JS problems. You can see it here, but it's outside of the focus of this question.
3. Var3: what happens if I don't re-assign constructor?
Actually you might not see the difference and this is what makes it a dangerous bug. Every function's prototype object has a constructor property so you can access the constructor from an instance.
function A() { }
// When you create a function automatically, JS does this:
// A.prototype = { constructor: A };
A.prototype.someMethod = function() {
console.log(this.constructor === A); // true
this.constructor.staticMethod();
return new this.constructor();
};
A.staticMethod = function() { };
It's not a best practice because not everybody knows about it, but sometimes it helps. But if you reassign the prototype...
A.prototype = {
someMethod = function() {
console.log(this.constructor === A); // false
console.log(this.constructor === Object); // true
this.constructor.staticMethod();
return new this.constructor();
}
};
A.prototype is a new object, a instance of Object than prototypes Object.prototype and Object.prototype.constructor is Object. Confusing, right? :P
So if you overwrite the prototype and don't reset the "constructor" property, it will refer to Object instead of A, and if you try to use the "constructor" property to access some static method you may get crazy.
I usually settle with returning an object with functions as properties:
var newCat = function (name) {
return {name: name, purr: function () {alert(name + ' purrs')}};
};
var myCat = newCat('Felix');
myCat.name; // 'Felix'
myCat.purr(); // alert fires
You can have inheritance by calling the newCat function and extend the object you get:
var newLion = function (name) {
var lion = newCat(name);
lion.roar = function () {
alert(name + ' roar loudly');
}
return lion;
}
If you want a global cats object:
var cats = (function () {
var newCat = function (name) {
return {
name: name,
purr: function () {
alert(name + ' is purring')
}
};
};
return {
newCat: newCat
};
}());
Now you can call:
var mySecondCat = cats.newCat('Alice');

is it possible to assign a prototype on an existing object in javascript?

if i have:
function Base (){
this.sayHi = function(){
alert('hi');
}
}
function Thing (val){
this.value = val;
}
var bob = new Thing("bob");
Is there some way I can now say that bob inherits from Base so that I could call:
bob.sayHi();
Basically have all the methods and properties of the Base class available on that instance?
A more relevant example with solution based on CMS's post:
Here Items could represent data returned by the server..
var Items = [
{ sku: 123, type:'buggy', title:'This is a title', description: 'this is a description' },
{ sku: 234, type: 'baby-monitor', title: 'This is a title 2', description: 'this is a description 2' }
]
function ItemMethods() {
this.BannerHTML = function () {
return '<div class="banner _item_' + this.type + '_' + this.sku + '"><h2>' +
this.title + '</h2><p>' +
this.description + '</p></div>';
};
}
Items.GetBySKU = function (code) {
for (var i = 0; i < Items.length; i++) {
if (Items[i].sku == code) {
return Items[i];
}
}
};
$.each(Items, function (i, item) {
ItemMethods.apply(item);
});
alert(Items.GetBySKU(234).BannerHTML());
Any further comments or solutions gladly accepted.. always interested in potential solutions to a problem ;-)
No. There is no way to assign a different [[prototype]] of an existing object, at least according the the specification. The [[prototype]] is the object resulting from the evaluation (aka "contained in") of the constructors prototype property at time of new object creation and cannot be reassigned later. (I wish it could be officially changed, but alas it is an unsupported operation and can generally be emulated via different methods.)
Some browsers/environments may choose to expose post-creation [[prototype]] assignment with non-standard approaches. The [[prototype]] object(s) can be modified (e.g. adding to String.prototype), or singleton functions can be added to the target object (see CMS's answer), or existing objects can be wrapped (essentially a dynamic "subclass") -- depending upon requirements and restrictions.
Also, there are no "classes" in Javascript: while "classical single-inheritance objected oriented classes" can be implemented in Javascript, I find it limiting to restrict oneself to the model or use such terminology in general. A language wants to be what it is.
Happy coding.
In javascript you can create a subclass of an other by setting the subclass' prototype to an instance of the base class:
function BaseClass() {
this.baseFunction = function() {
};
};
function SubClass() {
};
SubClass.prototype = new BaseClass;
SubClass.prototype.someFunction = function() {
};
// create an instance of SubClass
var obj = new SubClass;
// SubClass truly extends BaseClass
obj instanceof BaseClass // true
obj instanceof SubClass // true
// the instance has both methods of BaseClass and SubClass
typeof obj.someFunction // Function
typeof obj.baseFunction // Function
This is the equivalent of class SubClass extends BaseClass in java.
If you also modify the prototypes after that.
If you add functions and properties to the prototype of the object's constructor, the functions and properties will be available on all the instances.
Here is an example:
function Thing (val){
this.value = val;
}
var bob = new Thing("bob");
console.log(bob.foo); // undefined
Thing.prototype.foo = function() {
console.log('foo!');
};
console.log(bob.foo); // function()
bob.foo(); // foo!
Now if you want to extend all Thing instances with Base, you can do it like this:
var base = new Base;
for (var k in base) {
Thing.prototype[k] = base[k];
}
Or if you want to make Thing instances extend Base: (i.e. don't override methods that are already in Thing)
var base = new Base;
for (var k in base) {
if (Thing.prototype[k]) continue;
Thing.prototype[k] = base[k];
}
If you only want to extend a unique object instance, just assign to it:
var bob = new Thing("bob");
var base = new Base();
bob.sayHi = base.sayHi;
bob.sayHi();
You can also call a function in the context of an object, without even assign the function to the object:
var base = new Base();
base.sayHi.call(bob);
Note that the properties that you create within the constructor, have nothing to do with the constructor's prototype, they are own properties of the object you create using new Base();, they are not inherited. However, I think what you want to do it to apply the Base constructor function on the newly created object of Thing:
function Base (){
this.sayHi = function(){
alert('hi');
}
}
function Thing (val){
Base.apply(this, arguments);
this.value = val;
}
var bob = new Thing("bob");
bob.sayHi();
Note that bob will not inherit from Base (it won't have access to properties added to Base.prototype)
bob instanceof Base; // false
bob instanceof Thing; // true
Yes.
1) You can add Base's constructor definition directly to the bob instance
Base.call(bob);
bob.sayHi(); //hi
2) Or you can augment Thing.prototype with Base's constructor definition. The bob instance can access its prototype's new properties even if it was created before they were added
var bob = new Thing("bob");
Base.call(Thing.prototype);
bob.sayHi(); //hi

Javascript inheritance: call super-constructor or use prototype chain?

Quite recently I read about JavaScript call usage in MDC
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/call
one linke of the example shown below, I still don't understand.
Why are they using inheritance here like this
Prod_dept.prototype = new Product();
is this necessary? Because there is a call to the super-constructor in
Prod_dept()
anyway, like this
Product.call
is this just out of common behaviour? When is it better to use call for the super-constructor or use the prototype chain?
function Product(name, value){
this.name = name;
if(value >= 1000)
this.value = 999;
else
this.value = value;
}
function Prod_dept(name, value, dept){
this.dept = dept;
Product.call(this, name, value);
}
Prod_dept.prototype = new Product();
// since 5 is less than 1000, value is set
cheese = new Prod_dept("feta", 5, "food");
// since 5000 is above 1000, value will be 999
car = new Prod_dept("honda", 5000, "auto");
Thanks for making things clearer
The answer to the real question is that you need to do both:
Setting the prototype to an instance of the parent initializes the prototype chain (inheritance), this is done only once (since the prototype object is shared).
Calling the parent's constructor initializes the object itself, this is done with every instantiation (you can pass different parameters each time you construct it).
Therefore, you should not call the parent's constructor when setting up inheritance. Only when instantiating an object that inherits from another.
Chris Morgan's answer is almost complete, missing a small detail (constructor property). Let me suggest a method to setup inheritance.
function extend(base, sub) {
// Avoid instantiating the base class just to setup inheritance
// Also, do a recursive merge of two prototypes, so we don't overwrite
// the existing prototype, but still maintain the inheritance chain
// Thanks to #ccnokes
var origProto = sub.prototype;
sub.prototype = Object.create(base.prototype);
for (var key in origProto) {
sub.prototype[key] = origProto[key];
}
// The constructor property was set wrong, let's fix it
Object.defineProperty(sub.prototype, 'constructor', {
enumerable: false,
value: sub
});
}
// Let's try this
function Animal(name) {
this.name = name;
}
Animal.prototype = {
sayMyName: function() {
console.log(this.getWordsToSay() + " " + this.name);
},
getWordsToSay: function() {
// Abstract
}
}
function Dog(name) {
// Call the parent's constructor
Animal.call(this, name);
}
Dog.prototype = {
getWordsToSay: function(){
return "Ruff Ruff";
}
}
// Setup the prototype chain the right way
extend(Animal, Dog);
// Here is where the Dog (and Animal) constructors are called
var dog = new Dog("Lassie");
dog.sayMyName(); // Outputs Ruff Ruff Lassie
console.log(dog instanceof Animal); // true
console.log(dog.constructor); // Dog
See my blog post for even further syntactic sugar when creating classes. http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html
Technique copied from Ext-JS and http://www.uselesspickles.com/class_library/ and a comment from https://stackoverflow.com/users/1397311/ccnokes
The ideal way to do it is to not do Prod_dept.prototype = new Product();, because this calls the Product constructor. So the ideal way is to clone it except for the constructor, something like this:
function Product(...) {
...
}
var tmp = function(){};
tmp.prototype = Product.prototype;
function Prod_dept(...) {
Product.call(this, ...);
}
Prod_dept.prototype = new tmp();
Prod_dept.prototype.constructor = Prod_dept;
Then the super constructor is called at construction time, which is what you want, because then you can pass the parameters, too.
If you look at things like the Google Closure Library you'll see that's how they do it.
If you have done Object Oriented Programming in JavaScript, you will know that you can create a class as follows:
Person = function(id, name, age){
this.id = id;
this.name = name;
this.age = age;
alert('A new person has been accepted');
}
So far our class person only has two properties and we are going to give it some methods. A clean way of doing this is
to use its 'prototype' object.
Starting from JavaScript 1.1, the prototype object was introduced in JavaScript. This is a built in object that
simplifies the process of adding custom properties and methods to all instances of an object.
Let's add 2 methods to our class using its 'prototype' object as follows:
Person.prototype = {
/** wake person up */
wake_up: function() {
alert('I am awake');
},
/** retrieve person's age */
get_age: function() {
return this.age;
}
}
Now we have defined our class Person. What if we wanted to define another class called Manager which inherits some properties from Person. There is no point redefining all this properties again when we define our Manager class, we can just set it to inherit from the class Person.
JavaScript doesn't have built in inheritance but we can use a technique to implement inheritance as follows:
Inheritance_Manager = {};//We create an inheritance manager class (the name is arbitrary)
Now let's give our inheritance class a method called extend which takes the baseClass and subClassas arguments.
Within the extend method, we will create an inner class called inheritance function inheritance() { }. The reason why we are using this inner
class is to avoid confusion between the baseClass and subClass prototypes.
Next we make the prototype of our inheritance class point to the baseClass prototype as with the following code:
inheritance.prototype = baseClass. prototype;
Then we copy the inheritance prototype into the subClass prototype as follows: subClass.prototype = new inheritance();
The next thing is to specify the constructor for our subClass as follows: subClass.prototype.constructor = subClass;
Once finished with our subClass prototyping, we can specify the next two lines of code to set some base class pointers.
subClass.baseConstructor = baseClass;
subClass.superClass = baseClass.prototype;
Here is the full code for our extend function:
Inheritance_Manager.extend = function(subClass, baseClass) {
function inheritance() { }
inheritance.prototype = baseClass.prototype;
subClass.prototype = new inheritance();
subClass.prototype.constructor = subClass;
subClass.baseConstructor = baseClass;
subClass.superClass = baseClass.prototype;
}
Now that we have implemented our inheritance, we can start using it to extend our classes. In this case we are going to
extend our Person class into a Manager class as follows:
We define the Manager class
Manager = function(id, name, age, salary) {
Person.baseConstructor.call(this, id, name, age);
this.salary = salary;
alert('A manager has been registered.');
}
we make it inherit form Person
Inheritance_Manager.extend(Manager, Person);
If you noticed, we have just called the extend method of our Inheritance_Manager class and passed the subClass Manager in our case and then the baseClass Person. Note that the order is very important here. If you swap them, the inheritance
will not work as you intended if at all.
Also note that you will need to specify this inheritance before you can actually define our subClass.
Now let us define our subClass:
We can add more methods as the one below. Our Manager class will always have the methods and properties defined in the Person class because it inherits from it.
Manager.prototype.lead = function(){
alert('I am a good leader');
}
Now to test it let us create two objects, one from the class Person and one from the inherited class Manager:
var p = new Person(1, 'Joe Tester', 26);
var pm = new Manager(1, 'Joe Tester', 26, '20.000');
Feel free to get full code and more comments at:
http://www.cyberminds.co.uk/blog/articles/how-to-implement-javascript-inheritance.aspx

Categories

Resources