/**
* 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"
Related
I have two objects inst1, inst2 which are both instances of the same class. If I use
inst2 = JSON.parse(JSON.stringify(inst1));
now if I change values of properties of inst2, values in inst1 do not change. That is great.
But sadly methods of inst2 have disappeared. So if I do
inst2.method1();
I get the error
"inst2.method1 is not a function"
Is there some way I can copy the values in an instance without destroying methods?
(obviously I could laboriously copy each value. I am trying to avoid that because I am lazy.)
I have tried to follow typescript - cloning object but I cannot make it work-
Ok, I have played a little since the provided answers are not 100% clear.
If you want to have a shallow copy and copy the methods too, you can use Object.create.
Again: If your object is simple enough, Object.create will be sufficient for you
const originalPerson = new Person("John");
originalPerson.address = new Address("Paris", "France");
const newPerson = Object.create(originalPerson);
/// this will be true
const isInstanceOf = newPerson instanceof Person;
//this will change the property of the new person ONLY
newPerson.name = "Peter";
//methods will work
newPerson.someMethod();
//methods will work even on nested objects instances
newPerson.address.anotherMethod();
// BUT if we change the city on any of the instances - will change the address.city of both persons since we have done a shallow copy
newPerson.address.city = "Berlin";
I have created typescript playground (just remove the types) to show it works and the drawback with its usage - link to the playground
Another approach is the class itself to have a clone method and to be responsible for its own cloning logic. An example follows, along with a link to another playground
class Address {
constructor(city, country) {
this.city = city;
this.country = country;
}
clone() {
// no special logic, BUT if the address eveolves this is the place to change the clone behvaiour
return Object.create(this);
}
getAddressDetails() {
return `City: ${this.city} country ${this.country}`;
}
}
class Person {
constructor(name, address) {
this.name = name;
this.address = address;
}
clone() {
const newInstance = Object.create(this);
//clone all other class instances
newInstance.address = this.address.clone();
return newInstance;
}
getPersonDetails() {
//calling internally address.getAddressDetails() ensures that the inner object methods are also cloned
return `This is ${this.name}, I live in ${this.address.getAddressDetails()}`
}
}
const originalAddress = new Address("Paris", "France");
const originalPerson = new Person("John", originalAddress);
const clonedPerson = originalPerson.clone();
clonedPerson.name = "Peter";
clonedPerson.address.city = "Berlin";
clonedPerson.address.country = "Germany";
// Log to console
console.log(`Original person: ${originalPerson.getPersonDetails()}`)
console.log(`Cloned person: ${clonedPerson.getPersonDetails()}`)
You should use structured cloning, see this answer:
How do I correctly clone a JavaScript object?
The reason that your current code isn't working is because you are parsing a stringified json object. Json stringify will remove all of the methods of an object and only stringify the objects values.
I came back to this at a convenient point and made quite a bit of progress by combining some of the above answers. The general purpose cloner was getting quite ugly (see below) and still not working (for arrays of class-objects) when I realised that it would be impossible to write a general purpose cloner.
I use the term class-object to mean an object defined by a class.
If a class-object contains a variable which itself is type class-object, call it subObj, then the general purpose cloner cannot know whether 1) it should copy subObj or 2) it should create a new instance of subObj and copy into the sub-properties. The answer depends on the meaning in the class.
In the first case above subObj. is just a pointer to another instance of subObj.
Therefore I strongly agree with the second part of Svetoslav Petkov's answer that the "class itself [should] have a clone method and be responsible for its own cloning logic.".
For what it's worth this is as far as I got with a general purpose cloner (in TypeScript). It is adapted from the other answers and creates new instances of class-objects liberally:
public clone(): any {
var cloneObj = new (this.constructor as any)() as any;
for (var attribut in this) {
// attribut is a string which will take the values of the names of the propertirs in 'this'
// And for example, if aNumber is a property of 'this' then
// this['aNumber'] is the same as this.aNumber
if (typeof this[attribut] === "object") {
let thisAttr = this[attribut] as any;
let cloneAttr = cloneObj[attribut] as any;
if (this[attribut] instanceof Array) {
for (let i in thisAttr) {
cloneAttr[i] = thisAttr[i]; // **** will not work on arrays of objects!!
}
continue; // to next attrib in this
}
if (this[attribut] instanceof Date) {
cloneAttr.setTime(thisAttr.getTime());
continue; // to next attrib in this
}
try {
cloneObj[attribut] = thisAttr.clone();
//cloneObj[attribut] = this.clone(); // with this, (from https://stackoverflow.com/questions/28150967/typescript-cloning-object) stack just gets bigger until overflow
}
catch (err) {
alert("Error: Object " + attribut + " does not have clone method." +
"\nOr " + err.message);
}
} else {
cloneObj[attribut] = this[attribut];
}
}
return cloneObj;
}
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
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();
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)..
I currently use this style to create a js class like structure:
var JSClass = (function(){
console.log('JSClass Init');
//-- Set up private var and fnc here
var opt = {
width: 0,
height: 60
}
function _PrivateSum(g){
return (g * opt.width);
}
//-- Set up public access here
function JSClass(){ //the class constructor
//-- class attributes
}
//-- class methods
JSClass.prototype = {
getWidth : function(){
return _PrivateSum(opt.width);
},
setWidth : function(w){
console.log('JSClass setWidth: ' + w);
opt.width = w;
},
getHeight : function(){
console.log('JSClass getHeight');
return opt.height;
},
setHeight : function(h){
opt.height = h;
}
};
return JSClass;
}());
init by calling the following in another page:
var jc = new JSClass();
This is all good etc but if I then need to create a class that I would like to use several times on the same page:
var jc = new JSClass();
var jc2 = new JSClass();
At present if I change anything within the first "jc" then it also controls what is in the second "jc2".
So my question is how would I go about creating a fresh instance of my JSClass() class so that i can manipulate each one individually with out effecting the current one, similar to php classes etc
I believe I would need to somehow create a clone of the original but am not sure, or if there is a better way than above please feel free to inform me
much appreciated
I like to use a construction like this:
note: no new-statement.
//definition
var myClass = function() {
var abc = 1; //private properties
function f1() {...}; //private methods
return {
bar: function() {} //public function *with* access to private members and functions
};
};
//usage:
var myInstance1 = myClass();
var myInstance2 = myClass();
All Your class instances will use the same opt object, so changing it in one instance will change it for all other instances, too.
You'll have to move opt into the constructor function. The prototype functions than loose access to opt, of course.
If you want to use the functional approach for classes with private members, you have to give up the beauty of the prototype, and inheritance will be complicated. But you'll get real private members.
Crockford's "The Good Parts" is a reading I would recommend for these things.
Your variables ("opt") are STATIC (class variables - i.e. shared across all instances of the class) not instance variables. Instance variables are properties on the "this" object, which you create in the constructor and/or the two set(ter) functions you have. In setWidth,setHeight replace opt.width (or height) with this.width (or height) and remove the static var "opt".
Also, move _PrivateSum into the prototype object or you willo have trouble accessing the new instance variable just introduced - unless you call it using _PrivateSum.call(this, this.width), because when calling it as you do now "this" will be wrong, but if it's an instance method and you call it with this._PrivateSum(...) inside it "this" will point to the correct object.