JavaScript property inheritance - javascript

I'm trying to have a generic 'List' class, which will have:
Property: Items - which would be an array of 'what-ever'
Method: Add() - which would be abstract and implemented by the specific 'List' object
Method: Count() - which returns the number of 'items'
And then create sub-classes which will inherit from 'List':
// Class 'List'
function List(){
this.Items = new Array();
this.Add = function(){ alert('please implement in object') }
}
// Class CDList - which inherits from 'List'
function CDList(){
this.Add = function(Artist){
this.Items.push(Artist)
}
}
CDList.prototype = new List();
CDList.prototype.constructor = CDList;
// Create a new CDList object
var myDiscs = new CDList();
myDiscs.Add('Jackson');
myDiscs.Count() <-- this should be 1
// Create a second CDList object
var myDiscs2 = new CDList();
myDiscs2.Add('Walt');
myDiscs2.Add('Disney');
myDiscs2.Count() <-- this should be 2
...but this seems to create a shared 'Items' list for all 'CDList' instances. I need to somehow have a new inherited instance of the 'Items' list for each 'CDList' instance.
How can I do this?
*I'm using in this example the 'Items' list as an example. I'd like to be able to have in my sub-classes a new instance for any type of inherited property - not necessarily an Array object.

There is only one Array because you only create one. This array is attached to the prototype of "CDList" and therefore shared between all instances.
To solve this problem: don't attach it to the prototype, but to the instance. This can only be done at construction time:
// This is the constructor of the parent class!
function List() {
this.Items = new Array();
}
// Add methods to the prototype, not to the instance ("this")
List.prototype.Add = function() { alert('please implement in object'); };
// Constructor of the child
function CDList() {
List.call(this); // <-- "super();" equivalent = call the parent constructor
}
// "extends" equivalent = Set up the prototype chain
// Create a new, temporary function that has no other purpose than to create a
// new object which can be used as the prototype for "CDList". You don't want to
// call "new List();", because List is the constructor and should be called on
// construction time only. Linking the prototypes directly does not work either,
// since this would mean that overwriting a method in a child overwrites the
// method in the parents prototype = in all child classes.
var ctor = function() {};
ctor.prototype = List.prototype;
CDList.prototype = new ctor();
CDList.prototype.constructor = CDList;
// Overwrite actions
CDList.prototype.Add = function(Artist) {
this.Items.push(Artist);
};
Demo: http://jsfiddle.net/9xY2Y/1/
The general concept is: Stuff that each instance must have its own copy of (like the "Items" array in this case) must be created and attached to "this" (= the instance) at construction time, i.e. when doing new List() or new CDList(). Everything that can be shared across instances can be attached to the prototype. This essentially means that properties like the "Add" function are created exactly one time and are then used by all instances (what caused the original issue).
When linking prototypes, you must not directly link them (usually), e.g.:
CDList.prototype = List.prototype;
DVDList.prototype = List.prototype;
// Now add a new function to "CDList"
CDList.prototype.Foo = function() { alert('Hi'); };
Because the prototypes of the three functions "List", "CDList" and "DVDList" got directly linked to each other, they all point to one prototype object, and that is List.prototype. So, if you add something to CDList.prototype you actually add it to List.prototype - which also is the prototype of "DVDList".
var dvd = new DVDList();
dvd.Foo(); // <-- alerts "hi" (oops, that wasn't intended...)
What does the trick is to link the prototype to a new instance of the parent class:
CDList.prototype = new List();
This creates a new object of type "List()" with the special feature that the prototype of the function "List()" is linked to the new object, enabling you to call properties of the prototype directly on the object:
var l = new List();
alert( l.hasOwnProperty("Add") ); // <-- yields "false" - the object l has no
// property "Add"
l.Add("foo"); // <-- works, because the prototype of "List" has a property "Add"
However, remember that we intended to use the body of the function "List()" to create stuff like this array "Items" on a per-instance basis? It is the place where you put any "constructor" code, e.g.
function User(userId) {
$.getJSON('/user/' + userId, ...
}
function Admin() {}
Admin.prototype = new User( // ... now what?
One very clean solution is to use another function to create a prototype-object:
var ctor = function() {}; // <-- does nothing, so its super safe
// to do "new ctor();"
It is now okay to directly link the prototypes, because we will never add anything to ctor.prototype:
ctor.prototype = List.prototype;
If we then do:
CDList.prototype = new ctor();
the prototype of "CDList()" becomes a new object of type "ctor", that has no own properties but can be extended, e.g. by a new "Add" function:
CDList.prototype.Add = function() { /* CD specific code! */ };
However, if you do not add an "Add" property to this new prototype object, the prototype of "ctor()" kicks in - which is the prototype of "List()". And that's the desired behavior.
Also, the code in "List()" is now only executed whenever you do new List() or when you call it directly from another function (in a child class via List.call(this);).

Try this:
function CDList(){
List.call( this )
this.Add = function(Artist){
this.Items.push(Artist)
}
}
You need to call the superconstructor...
I like this article of the MDN network about JavaScript inheritance. I tried this method/technique and it works very fine in all browsers I tested (Chrome, Safari, Internet Explorer 8+, and Firefox)..

Related

create subclass object with reference to existing object

In node.js (Javascript) I have two classes, class mainclass and subclass, where subclass inherites from mainclass.
In an other module (other class, other .js file) i have an array of objects from class mainclass:
myArray[0] = new mainclass();
myArray[1] = new mainclass();
//etc..
On runtime, i want to create a new subclass object, and set its reference to the one in myArray[0], so that myArrayis not changed, but myArray[0] then returns the new subclass object.
And i want to do this in the mainclass, so that the array is not changed, but the reference in the array points now to an other object (the new subclass object). In fact i want to do something like
this = new subclass();
in a method in mainClass
mainClass.prototype.changeType = function(){
this = new subclass();
}
which of course doesnt work because you cant assign value to this.
You could "simulate" pointers if you are ready to access your objects through indexes. As you can see below, whatever object reference is at index 0, it remains available :
function Person (name) { this.name = name; };
Person.prototype.whoami = function () { return this.name };
memory = [];
memory.push(new Person("Hillary Clinton"));
memory[0].whoami(); // "Hillary Clinton"
memory[0] = new Person("Donald Trump");
memory[0].whoami(); // "Donald Trump"
Good luck though... x-D

Can a javascript function be a class and an instance of another object?

If you look at this code:
function supportAggregate(Meanio) {
Meanio.prototype.aggregated = function(ext, group, callback) {
// Aggregated Data already exists and is ready
if (Meanio.Singleton.config.clean.aggregate === false){
return callback('');
}
if (aggregated[group][ext].data) return callback(aggregated[group][ext].data);
// No aggregated data exists so we will build it
sortAggregateAssetsByWeight();
// Returning rebuild data. All from memory so no callback required
callback(aggregated[group][ext].data);
};
Meanio.prototype.aggregatedsrc = function(ext, group, callback) {
// Aggregated Data already exists and is ready
if (Meanio.Singleton.config.clean.aggregate !== false){
if(ext==='js'){
if(group==='header'){
return callback(['/modules/aggregated.js?group=header']);
}else{
return callback(['/modules/aggregated.js']);
}
}else if(ext==='css' && group==='header'){
return callback(['/modules/aggregated.css']);
}
return callback([]);
}
if (aggregated[group][ext].src) return callback(aggregated[group][ext].src);
// No aggregated data exists so we will build it
sortAggregateAssetsByWeight();
// Returning rebuild data. All from memory so no callback required
callback(aggregated[group][ext].src);
};
// Allows rebuilding aggregated data
Meanio.prototype.rebuildAggregated = function() {
sortAggregateAssetsByWeight();
};
Meanio.prototype.Module.prototype.aggregateAsset = function(type, asset, options) {
options = options || {};
if (!options.inline && !options.absolute && !options.url) {
asset = path.join(Meanio.modules[this.name].source, this.name, 'public/assets', type, asset);
}
Meanio.aggregate(type, asset, options, Meanio.Singleton.config.clean);
};
Meanio.onModulesFoundAggregate = function(ext, options) {
var config = Meanio.Singleton.config.clean;
var aggregator = new Aggregator(options, false, config);
for (var name in Meanio.modules) {
aggregator.readFiles(ext, path.join(process.cwd(), Meanio.modules[name].source, name.toLowerCase(), 'public'));
}
};
Meanio.aggregate = function(ext, asset, options, config) {
var aggregator;
options = options || {};
if (!asset) {
return;
}
aggregator = new Aggregator(options, true, config);
if (options.inline) return aggregator.addInlineCode(ext, asset);
else if (options.url) return aggregator.getRemoteCode(ext, asset);
else if (options.singlefile) return aggregator.processDirOfFile(ext, asset);
else return aggregator.readFile(ext, path.join(process.cwd(), asset));
};
Meanio.prototype.aggregate = Meanio.aggregate;
}
module.exports = supportAggregate;
(https://github.com/linnovate/meanio/blob/master/lib/aggregation.js#L213)
You can see that there are two types of functions for Meanio that are created. Also, by the way, you can see where this is instantiated here: https://github.com/linnovate/meanio/blob/master/lib/mean.js#L114
But I'm just confused. Sometime, Meanio functions are defined like this:
Meanio.prototype.myfunction = function() {}
and sometimes they are defined like this:
Meanio.myfunction = function() {}
I just don't get it; although I have a feeling that dependency injection is somehow involved.
How can this be? How can an object be both a class and an instance of itself?
This code is very confusing to me, and I would really appreciate it if someone could shed some light on this for me. I'm not asking you to heavily research the code, but if you could give me a general understanding, that would be great.
Thanks in advance!
How can an object be both a class and an instance of itself?
That's not what's going on here. The object passed to the function is an instance.
The function does however modify both the instance that you pass to it, and the class of that instance.
If you create two instances of the same class, and pass one of them to the function, the other instance is not modified, but the class that is common to them is modified. Example:
function MyClass() {}
var a = new MyClass();
var b = new MyClass();
supportAggregate(a);
Now both a.rebuildAggregated and b.rebuildAggregated exist, as that is added to the class. The a.onModulesFoundAggregate exists because it's added to the instance, but b.onModulesFoundAggregate doesn't exist.
(Note: The example won't actually work, as there is more going on. The class has to have some more properties to work with that function, the example is only to show the difference between properties added to the prototype and to the instance.)
Let's say I have a constructor
// First I will define a constructor
function MyClass() {
this.classproperty = 1;
}
In Javascript the constructor is also an object instance. When I use "this" keyword inside a constructor I'm telling that I want to create a new property inside a special object present in all javascript objects called prototype.
// Then I add a new property without using prototype obj
MyClass.newProperty = 2;
alert(MyClass.classproperty); // alert 1
alert(MyClass.newProperty); // alert 2
// It will work because I'm using the MyClass main Object
When you create a new instance from Myclass Obj. The new created object will inherit the prototype object from parent (the one used to instantiate), but not the properties added straight to MyClass obj:
var instance = new MyClass();
alert(instance.newProperty); // undefined because the new instance will
// inherit only what is inside prototype obj
I have to add it to the prototype object in order to new instances inherit the property;
Myclass.prototype.newProperty = 2;
var instance = new Myclass();
alert(instance.newProperty) // alert 2

Javascript Object Inheritance how to get the Child instance name

/**
* adds an entry to the list
*
* #param data {Object}
* #return this
*/
ElementList.prototype.addEntry = function(data){
if (!data) return this;
data['type'] = this.children_type;
// add the entry to the current elements
this.element_list.push(new FavoriteEntry(data));
this.refresh();
return this;
};
/**
* favorites extend ElementList
*
* #param setting_list
* #constructor
*/
function FavoriteList(setting_list){
ElementList.call(this, setting_list);
}
FavoriteList.prototype = new ElementList();
FavoriteList.constructor = FavoriteList;
So this a short code snipplet of an educational project of mine.
What I want to do is reduce repeating code so I created a generic ElementList object
So
the Example FavoriteList inherties the parent Objects prototype
The constructors is pointing to the Childobject
the Parent constructor is called within the child.
That works just perfectly fine my problem is
// add the entry to the current elements
this.element_list.push(new FavoriteEntry(data));
This should create a new instance of an Object BASED on the CHILD so therefore I need to get the name of the child instance that's calling the parent method
i tried
- this.constructor (point to the parent)
- this.constructor.name
- this instanceof FavoriteList (works)
since I DON'T want to pass a name and i think iterating through instanceof "options" is not really smart.
I would ask for some insights how I can access the childs instance name in the parent elements method body.
Please I only need an explicit answer to this!! I already read workarounds! If It's not possible just say so :)
thx in advance :)
this.element_list.push(new FavoriteEntry(data));
This should create a new instance of an Object BASED on the CHILD so
therefore I need to get the name of the child instance that's calling
the parent method
No, you don't seem to need to know the name. All you need is a helper function to generate new Entry instances, that can be overwritten to generate more specific entries. Maybe you're already doing that by passing a children_type with the data…
i tried - this.constructor (point to the parent)
It should work if you had set the constructor correctly. Change your code to
FavoriteList.prototype.constructor = FavoriteList;
// ^^^^^^^^^^
Also, you might want to use Object.create instead of new to set up the prototype chain.
I'm not sure if I fully understand but the code new FaforiteEntry should create either a FororiteEntry or another type based on the current object type.
Maybe the following example could help you out:
var ElementList = function(args) {
this.element_list = [];
}
ElementList.prototype.addEntry = function(args) {
this.element_list.push(new this.entryType(args.val));
};
//will create element_list items of type String
ElementList.prototype.entryType = String;
function FavoriteList(args) {
ElementList.call(this, args);
}
FavoriteList.prototype = Object.create(ElementList.prototype);
FavoriteList.constructor = FavoriteList;
//will create element_list items of type Array
FavoriteList.prototype.entryType = Array;
//adding entries to f would create items of type Array
var f = new FavoriteList();
f.addEntry({val: 2});
console.log(f.element_list);//[[undefined, undefined]]
//adding entries to e would create items of type String
var e = new ElementList();
e.addEntry({val: 2});
console.log(e.element_list);//[ String { 0="2"...
Simple code example:
function Parent(){
// custom properties
}
Parent.prototype.getInstanceName = function(){
for (var instance in window){
if (window[instance] === this){
return instance;
}
}
};
var child = new Parent();
console.log(child.getInstanceName()); // outputs: "child"

Value of constructor and prototype gets changed after over writing the prototype object. Why?

I have the Director() function. I have created 2 instances AlfredH and JohnD out of Director() constructor. I did not write the prototype object.
function Director(){
this.genre = "Thriller";
}
var AlfredH = new Director();
var JohnD = new Director();
If I check the values of JohnD.constructor; and JohnD.constructor.prototype; I get Director() and Object() respectively.
But, if I add properties to prototype object of Director() like the below:
function Director(){
this.genre = "Thriller";
}
Director.prototype = {
noir: true
};
var AlfredH = new Director();
var JohnD = new Director();
and if I check the values of JohnD.constructor; and JohnD.constructor.prototype; I get Object() and Object() respectively. Can anyone explain this behavior? and the same can be extended to the value of JohnD.constructor.prototype.constructor;
var a = {
value:22;
}
then
var a = {
somethingelse:0
}
Can you guess what a.value is?
You are overwriting the prototype with another object.
Then add to that that
console.log({}.constructor)===Object;//=true
Maybe try adding it like this:
Director.prototype.noir = true;
Note that anything on the prototype is shared among instances, this is a good thing because it saves memory and instantiate the object quicker with less cpu.
When assigning a new value the value is assigned to the instance but when manipulating the value through functions it affects all instances
Director.prototype.someArray=[];
var d1=new Director();
var d2=new Director();
d1.someArray.push(22);
console.log(d2.someArray);//=[22]
More info on prototype here: https://stackoverflow.com/a/16063711/1641941

Object Oriented JavaScript programming

I have been trying to learn OOP with JavaScript before I start attempting to learn backbone.js.
I want to be able to data bind but I can't seem to get it to work.
I've just made a simple protoype of a budget website that you can put in a budget and input how much you've spent, and it will show if you've gone over.
function BudgetItem(spent, budget){
this.setSpent = function(spent){
this.spent = spent;
}
this.setBudget = function(budget){
this.budget = budget;
}
this.getSpent = function(){
return this.spent;
}
this.getBudget = function(){
return this.budget;
}
}
function BudgetType(type){
this.getType = function(){
return type;
}
}
BudgetType.prototype = new BudgetItem();
$(document).ready(function(){
var food = new BudgetType('food');
$('.budget').html(food.getBudget());
$('.editbudget').change(function(){
food.setBudget($('.editbudget').data())
});
})
That's my code thus far. I'm not sure if I'm doing it right. Am I supposed to extend things? Also, can someone explain how to dynamically data bind without a library?
First I'll give you some theory. A Javascript function is a dynamic object, just like Object is, and a new instance can be created using the new keyword much like you are doing in your listener. When this happens, the function itself will run as a constructor while the this keyword will be bound to the newly created object. What you're doing above then is in fact adding new properties on the fly as you're passing in their values for the first time... which is fine, but not very clear to another reader.
Now for the tricky part. Every function has a link to a "hidden" Prototype object. This is an anonymous (not accessible by name) object created by the JavaScript runtime and passed as a reference to the user object through the prototype property. This Prototype object also has a reference to the function through its constructor property. To test what I'm saying for yourself, try the following:
BudgetItem.prototype.constructor === BudgetItem // true
Putting it all together, you can now think of functions as constructors to (hidden) classes that are created for you behind the scenes, accessible through the function's prototype property. So, you could add the fields to the Prototype object directly as so:
function BudgetItem(spent) {
this.spent = spent
}
BudgetItem.prototype.setSpent = function(spent) { this.spent = spent };
BudgetItem.prototype.getSpent = function() { return this.spent };
Another problem is inheritance and passing parameters to the constructor. Again, your version is valid but you lose the ability to pass the spent and budget values when initializing a BudgetType. What I would do is forget prototypes and go:
function BudgetType(type, spent) {
var instance = new BudgetItem(spent);
instance.type = type;
return instance;
}
This is close to what Scott Sauyet suggested above but more powerful. Now you can pass both parameters (and more) and have a more complicated inheritance tree.
Finally, what you can do is create private (or pseudo-private, more accurately) properties by providing a getter to an otherwise automatic variable (one passed as an argument or initialised inside the function). This is a special feature of the language and it works like so:
function BudgetType(type, spent) {
var instance = new BudgetItem(spent);
instance.getType = function() {
return type;
}
return instance;
}
Now you can access the 'type' passed in the constructor by obj.getType() but cannot override the initial value. Even if you define obj.type = 'New Value' the getType() will return the initial parameter passed because it has a reference to another context which was created when the object was initialised and never got released due to the closure.
Hope that helps...
if you want all instances of objects to reference the same members/values you can use a closure:
// create a constrctor for you object wrapped in a closure
myCon = (function() {
// define shared members up here
var mySharedObj = new function () {
this.member = "a";
}();
// return the actual constructor
return function () {
this.mySharedObj = mySharedObj;
}
}());
// create two instances of the object
var a = new myCon();
var b = new myCon();
// Altering the shared object from one
a.mySharedObj.member = "b";
// Alters it for all
console.log(b.mySharedObj.member);
If you want to build objects from other objects(sort of like other languages' class whatever extends baseClass), but do not want them to share values via reference(instead a clone of values), you can use something like the following:
Object.prototype.extendsUpon = (function (_prop, _args) {
return function (base) {
for (var key in base) {
if (_prop.call(base, key)) {
this[key] = base[key];
}
}
function con(child){
this.constructor = child;
}
con.prototype = base.prototype;
this.prototype = new con(this);
this.__base__ = base.prototype;
var args = _args.call(arguments);
args.shift();
base.constructor.apply(this, args);
}
}(Object.prototype.hasOwnProperty, Array.prototype.slice));
Then to build objects ontop of objects:
// Base Object Constructor
function Fruit(name) {
this.fruitname = name;
}
Fruit.prototype.yum = function() {
return "I had an " + this.fruitname;
}
// Object constructor that derives from the Base Object
function Favorite() {
// Derive this object from a specified base object:
// #arg0 -> Object Constructor to use as base
// #arg1+ -> arguments passed to the BaseObject's constructor
this.extendsUpon(Fruit, "apple");
// From here proceed as usual
// To access members from the base object that have been over-written,
// use "this.__base__.MEMBER.apply(this, arguments)"
}
Favorite.prototype.yum = function() {
return this.__base__.yum.apply(this) + " and it was my favorite";
}
var mmm = new Favorite();
// Outputs: "I had an apple and it was my favorite"
mmm.yum();

Categories

Resources