I'm looking for the best way to update in object instance, for the example in this case myParentObjects's name property. The only way I understand to achieve this would be to pass reference of the parent object to the child object instance as a parameter into either new myChildObj(this,name) in the constructor, or a method of the myChildObj instance like myChildObj.updateParentProperty(name).
I can't imagine a child object nested 4-5 levels down, and having to update properties on it's parents passing (parent1,parent2,parent3,etc) it's params, that would be a managing nightmare! there must be a better way to update parent properties!
function myParentObj(){
this.name = 'jordan'
this.names = ['jordan','danny','cassie'];
this.init=()=>{
this.names.forEach((name)=>{
var childObj = new myChildObj(this,name);
childObj.updateParentProperty();
})
}
}
function myChildObj(parentObj,name){
this.parent = parentObj;
this.name = name;
this.updateParentProperty=()=>{
this.parent.name = this.name;
};
}
function init(){
var parentObj = new myParentObj();
parentObj.init();
}
document.addEventListener('DOMContentLoaded',init);
Question: What is the best method to update parent object parameters?
What puzzles me is this:
this.init=()=>{
this.names.forEach((name)=>{
var childObj = new myChildObj(this,name);
childObj.updateParentProperty();
})
}
because as soon as all three children are created, your parent will have a name of cassie (since that's the last child calling updateParentProperty). That and your tags is why I'm inclined to claim that you're looking for classical inheritance, like so:
Classical inheritance
function myParentObj (name) {
this.name = name;
this.names = ['jordan','danny','cassie'];
this.init=()=>{
this.names.forEach((name)=>{
var childObj = new myChildObj(name);
console.log(childObj);
});
}
}
function myChildObj (name) {
myParentObj.call(this, name);
// Other stuff, only available for myChildObj
}
function init(){
var parentObj = new myParentObj();
parentObj.init();
}
document.addEventListener('DOMContentLoaded',init);
var parent = new myParentObj();
console.log(parent);
Which would mean the child inherits properties from its parent, but would of course not update them for the parent when their own name changes.
Some simple observer pattern
Okay, your example just has a single property which change should be noticed. I think an observer pattern like in the example below is a bit too much, but for larger applications it's quite useful.. the observer pattern is not distinct to JavaScript only (see enter link description here). Basically, you have an object which changes shall be monitored (observable) and one or many objects which want to perform actions depending on these changes (observers). The implementation can be quite simple (like my demonstration) or really complex. The basic idea is for the observable to maintain a list of its observers. The example uses an own, simple implementation. There are also libraries available, we're using knockout, which offers a MVVM structure for your page and can even update the DOM depending on changes.
function myParentObj(){
this.name = 'jordan'
this.names = ['jordan','danny','cassie'];
this.init=()=>{
this.names.forEach((name)=>{
var childObj = new myChildObj(name);
childObj.subscribe('name', newValue => this.name = newValue);
})
}
}
function myChildObj(name){
this.subscribers = {};
Object.defineProperty(this, 'name', {
set: newValue => {
this._name = newValue;
this.notify('name');
},
get: () => this._name
});
this.subscribe = function (prop, callback) {
if (!this.subscribers.hasOwnProperty(prop)) {
this.subscribers[prop] = [];
}
this.subscribers[prop].push(callback);
};
this.notify = function (prop) {
if (this.subscribers[prop]) {
this.subscribers[prop].forEach(callback => callback(this[prop]));
}
};
}
function init(){
var parentObj = new myParentObj();
parentObj.init();
}
document.addEventListener('DOMContentLoaded',init);
var parent = new myParentObj();
var child = new myChildObj('bob');
child.subscribe('name', newName => parent.name = newName);
console.log(parent.name);
child.name = 'cassie';
console.log(parent.name);
Using Object.assign
You could also use Object.assign, which will copy all enumerable properties from one object to another. You should note, though, that it will copy all properties, even those you might not want to change.
function myParentObj(){
this.name = 'jordan'
this.names = ['jordan','danny','cassie'];
this.init=()=>{
this.names.forEach((name)=>{
var childObj = new myChildObj(this,name);
childObj.updateParentProperty();
})
}
}
function myChildObj(name){
this.name = name;
}
var parent = new myParentObj();
var child = new myChildObj('cassie');
Object.assign(parent, child);
console.log(child);
Classical inheritance
I see you mixed in some ES6, so you might be looking for an answer using the ES6 standards of object-oriented inheritance like so:
class Parent {
constructor(name = 'jordan') {
this.name = name
this.names = ['jordan', 'danny', 'cassie']
}
init() {
return this.names.map((name) => {
return new Child(name)
})
}
}
class Child extends Parent {
constructor(name) {
super(name)
// other stuff for Child
}
}
let parent = new Parent()
console.log(parent)
let children = parent.init()
children.forEach((child) => console.log(child))
In ES5, inheritance was usually implemented like this instead, though this is not exactly what ES6 class does, since ES6 does things like making instance methods non-enumerable, etc.
function Parent(name) {
name = arguments.length > 0 ? name : 'jordan'
this.name = name
this.names = ['jordan', 'danny', 'cassie']
}
// member methods
Parent.prototype = {
init: function init() {
return this.names.map(function (name) {
return new Child(name)
})
}
}
function Child(name) {
// basically, super(name)
Parent.call(this, name)
// other stuff for Child
}
// extending Parent
Child.prototype = Object.create(Parent.prototype)
Parent.prototype.constructor = Parent
var parent = new Parent()
console.log(parent)
var children = parent.init()
children.forEach(function (child) { console.log(child) })
Below is almost exactly the ES5 equivalent for the ES6 code in the first example:
function Parent() {
var name = arguments.length > 0 && name !== undefined ? name : 'jordan'
this.name = name
this.names = ['jordan', 'danny', 'cassie']
}
// member methods
Object.defineProperties(Parent.prototype, {
init: {
configurable: true,
value: function init() {
return this.names.map(function (name) {
return new Child(name)
})
},
writable: true
}
})
function Child(name) {
// basically, super(name)
Parent.call(this, name)
// other stuff for Child
}
// extending Parent
Child.prototype = Object.create(Parent.prototype, {
constructor: {
configurable: true,
value: Child,
writable: true
}
})
var parent = new Parent()
console.log(parent)
var children = parent.init()
children.forEach(function (child) { console.log(child) })
EventEmitter
After looking at the comments below the other answer, it appears you are most interested in observer-type patterns. EventEmitter, defined natively in Node.js, is the most widely used implementation of the observer pattern. Below is a polyfill demo of it for client-side JavaScript:
class Parent extends EventEmitter {
constructor(name = 'jordan') {
super()
this.name = name
}
}
class Child {
constructor(parent, name = parent.name) {
this.parent = parent
this._name = name
}
set name(name) {
console.log('setting child name')
this._name = name
this.parent.emit('name', name)
}
get name() {
return this._name
}
}
let parent = new Parent()
parent.on('name', function (name) {
console.log('setting parent name')
this.name = name
}.bind(parent))
let child = new Child(parent)
console.log('parent', parent.name)
console.log('child', child.name)
child.name = 'danny'
console.log('parent', parent.name)
console.log('child', child.name)
<script src="https://cdn.rawgit.com/mudge/5830382/raw/a4bc230f5bce01ea9a34b0d42247256531b97945/eventemitter.js"></script>
Proxy
Another neat API that JavaScript has is called Proxy, which allows metaprogramming, that is, redefining how JavaScript works in isolated cases:
var parent = {}
var child = {}
var proxy = new Proxy(child, {
set: (target, property, value) => {
// target === child
target['child-' + property] = typeof value + ' ' + value
parent['parent-' + property] = typeof value + ' ' + value
return value
}
})
proxy.name = 'jordan'
proxy.age = 54
console.log(parent)
console.log(child)
Related
How to access a method from the parent prototype in the child as we can with classes?
In a class when we have a method in the parent class we can access the same in the child class. In the prototype way of doing the same, I'm not able to access the parent prototype method
With class:
class Person {
constructor(name, id){
this.name = name;
this.id = id;
}
printDetails (){
console.log(`Printing details in parent class :${this.name} : ${this.id}`);
}
}
class Employee extends Person {
constructor(name, id, salary){
super(name, id);
this.salary = salary;
}
employeeInfo(){ // this will exist in the prototype of Employee class, not in the instance.
return `${this.name} : ${this.id} : ${this.salary}`
}
}
const a = new Employee('Mary', 1, 123456);
// console.log(a.employeeInfo())
// a.printDetails();
With Function and prototype:
let PersonF = function(name, id){
this.name = name;
this.id = id;
}
PersonF.prototype.getDetails = function(){ // Dont use arrow here, the this for the arrow is window, not the this of the object
console.log(`Printing details in parent in function way :${this.name} : ${this.id}`);
}
let pers = new PersonF('Person', 111);
// pers.getDetails();
let EmployeeF = function(name, id, salary){
PersonF.call(this, name, id); // this is same as super in class. here first param will take the this of the context and then other params
this.salary = salary;
}
Object.setPrototypeOf(EmployeeF, PersonF.prototype); // This is same as extends in class.
EmployeeF.prototype.printDetails = function(){
console.log(`${this.name} : ${this.id} : ${this.salary}`);
}
const emp = new EmployeeF('John', 1 , 10000000);
// emp.printDetails();
emp.getDetails(); // Getting an error here.
You can make sure, that EmployeeF inherits it's prototype from PersonF by using Object.create with PersonFs Prototype:
EmployeeF.prototype = Object.create(PersonF.prototype)
let PersonF = function(name, id){
this.name = name;
this.id = id;
}
PersonF.prototype.getDetails = function(){
console.log(`Printing details in parent in function way :${this.name} : ${this.id}`, this); // added log of `this`
}
let pers = new PersonF('Person', 111);
let EmployeeF = function(name, id, salary){
PersonF.call(this, name, id);
this.salary = salary;
}
EmployeeF.prototype = Object.create(PersonF.prototype) // create Employees Prototype from Persons
EmployeeF.prototype.printDetails = function(){
console.log(`${this.name} : ${this.id} : ${this.salary}`);
}
const emp = new EmployeeF('John', 1 , 10000000)
emp.getDetails();
This is also (part of) what Babel does when you target <IE10:
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
Object.defineProperty(subClass, "prototype", {
writable: false
});
if (superClass) _setPrototypeOf(subClass, superClass);
}
Problem
Your problem is :
Object.setPrototypeOf(EmployeeF, PersonF.prototype);
This method is made for objects used as instances, and not objects used as classes; it actually sets the __proto__ attribute – for instances –, and not the prototype attribute of a class.
You can see this in this polyfill:
Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
obj.__proto__ = proto;
return obj;
}
Then, getDetails is actually accessible like this: EmployeeF.getDetails() (which calls behind the scene EmployeeF.__proto__getDetails().
Solution
I suggest you this:
function Person(name) {
this.name = name;
}
Person.prototype.print = function () {
console.log('I am', this.name);
}
function Employee(name, id) {
this.name = name;
this.id = id;
}
// What has changed !
Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Person;
Employee.prototype.work = function() {
console.log(this.id, this.name, 'is working...');
}
var e = new Employee('Michel', 43);
e.print(); // 'I am Michel'
Explanation
The objective here, is to say "The prototype of the prototype of Employee is the prototype of Person", ie Employee.prototype.prototype = Person.prototype.
Why prototype.prototype ?
Because the prototype of Employee must contains the inheritable methods of Employee, not Person's ones; so the inherited methods of Person will be at the NEXT prototype in the prototypes chain.
Details
Object.create(a_prototype); return an empty object, but sets the prototype of this object to a_prototype.
So, first, we create the prototype of Employee to an empty object, but having Person.prototype as its prototype:
Employee.prototype = Object.create(Person.prototype);
Now, it remains just one problem: Employee.prototype has no constructor! Then, we simply add it via:
Employee.prototype.constructor = Person;
Thanks to this, we can see that Employee inherits from Person through Employee.prototype.constructor.
To see this, just try to print an instance of Person: the constructor is at person_instance.__proto__.constructor; then try to do the same thing with a new Employee, without the code above: no constructor in the prototype!
Solution 2
This is a simplified solution of what does TypeScript transpiler to convert classes from ES6 to ES5:
Object.setPrototypeOf(Employee, Person);
function PrototypeOfEmployee() {
this.constructor = Employee;
}
PrototypeOfEmployee.prototype = Person.prototype;
Employee.prototype = new PrototypeOfEmployee();
I am trying to access parent's method in child's constructor as following:
file1.js
var ParentClass = function(arg) {
this.arg = arg;
this.result = {};
};
ParentClass.prototype = {
constructor: ParentClass,
isActive: function(condition) {
return new Date(condition.valid).getTime() <= Date.now())
}
};
module.exports = ParentClass;
file2.js
var ParentClass = require('file1');
var ChildClass= function(arg) {
ParentClass.apply(this, arguments);
this.init();
};
ChildClass.prototype = Object.create(ParentClass.prototype);
ChildClass.prototype.constructor = ChildClass;
ChildClass.prototype = {
init: function() {
this.result = this.arg.validity
.filter(function(elem) {
return this.isActive(elem)
}.bind(this));
}
}
module.exports = ChildClass;
file3.js
var ChildClass= require('file2.js');
var instance = new ChildClass(param);
initializing such instance gives me
TypeError: this.isActive is not a function
at Object.<anonymous> (file2.js)
at Array.filter (native)
at Object.ChildClass.init (file2.js)
Help and explanation appreciated. Thank you!
You have two separate assignments to ChildClass.prototype. One will override the other. Instead, you need to first initialize your prototype with Object.create() as you are doing and then you need to ADD new methods to it, not assign to the whole prototype, thus replacing everything you just put there.
These are the two conflicting statements:
ChildClass.prototype = Object.create(ParentClass.prototype);
ChildClass.prototype = {...};
One common way to fix this is to use Object.assign() to copy your methods onto the existing prototype:
Object.assign(ChildClass.prototype, {
init: function() {
this.result = this.arg.validity
.filter(function(elem) {
return this.isActive(elem)
}.bind(this));
}
});
This will copy each of your methods over to the already existing prototype object and this method is more commonly used when you have lots of methods.
You can also just assign the new methods one at a time (more commonly used when you have just a few methods):
ChildClass.prototype.init = function() {
this.result = this.arg.validity.filter(function(elem) {
return this.isActive(elem)
}.bind(this));
}
You are redefining ChildClass.prototype as a new object, which overwrites your assignment using Object.create() a few lines prior. Instead, simply define the init method on it.
Try this:
var ParentClass = function(arg) {
this.arg = arg;
this.result = {};
};
ParentClass.prototype = {
constructor: ParentClass,
isActive: function(condition) {
return new Date(condition.valid).getTime() <= Date.now();
}
};
var ChildClass = function(arg) {
ParentClass.apply(this, arguments);
this.init();
};
ChildClass.prototype = Object.create(ParentClass.prototype);
ChildClass.prototype.constructor = ChildClass;
ChildClass.prototype.init = function() {
this.result = this.arg.validity
.filter(function(elem) {
return this.isActive(elem);
}.bind(this));
};
var instance = new ChildClass({
validity: [{
valid: "01/01/17"
}]
});
console.log(instance);
I am thinking about simple problem. I have given an class for example Model
class Model {
constructor(parameters = {}) {
this.id = parameters.id;
}
}
so as you can see we can create new Model objects like: let model = new Model(). More complex example would look like this:
//we have some data given from API maybe?
let parameters = {id: 1};
let model = new Model(parameters );
And here we are at the point where i started to wander What if the object with given id already exists ?
The question is what pattern should i use to instantiate object with given id only once?
Let's go further: what if we will get nested objects with circural references ? Let assume we got another class called AnotherModel and our code looks like:
class Model {
constructor(parameters = {}) {
this.id = parameters.id;
this.anotherModel= nulld;
if (parameters.anotherModel) {
this.anotherModel= parameters.anotherModel instanceof AnotherModel
? parameters.anotherModel
: new AnotherModel(parameters.anotherModel);
}
}
}
class AnotherModel {
constructor(parameters = {}) {
this.id = parameters.id;
this.models = [];
if (parameters.models) {
for (let i = 0; i < parameters.models.length; i++) {
let model = parameters.models[i];
model.anotherModel= this;
this.models.push(new Model(model));
}
}
}
}
So AnotherModel contains a collection of Models and Model object contains reference to AnotherModel.
What is the nice way to resolve this issue ? What we want to achive is to have only one object with the same id.
What i was thinking is to do some kind of ObjectPool where i will store all objects for given class or classes and when new object is instantiated our pool would create a new one if it does not exist or return the existing one?
But here is a little disadventage, if for example we already have written some code we would have to refactore and change the way we instatiate them from new Model() to ObjectPool.get(Model, parameters)?
What are your ideas ?
You could use an object pool (either on the class or outside of it) to keep track of your instances. By defining it in the constructor, you can still instantiate models with:
new Model();
new AnotherModel();
If the id already exists in the pool, you can just return the existing instance.
Outside of the class:
const modelPool = {};
class Model {
constructor(parameters = {}) {
if (modelPool[parameters.id] instanceof Model) {
return modelPool[parameters.id];
}
modelPool[parameters.id] = this;
this.id = parameters.id;
this.anotherModel= null;
// ...
}
}
const anotherModelPool = {};
class AnotherModel {
constructor(parameters = {}) {
if (anotherModelPool[parameters.id] instanceof AnotherModel) {
return anotherModelPool[parameters.id];
}
anotherModelPool[parameters.id] = this;
this.id = parameters.id;
this.models = [];
//...
}
}
Or as a (non-enumerable, non-writeable, non-configurable) property on the class (not the instance):
class Model {
constructor(parameters = {}) {
if (Model.pool[parameters.id] instanceof Model) {
return Model.pool[parameters.id];
}
Model.pool[parameters.id] = this;
this.id = parameters.id;
this.anotherModel= null;
//...
}
}
Object.defineProperty(Model, 'pool', {
value: {}
});
class AnotherModel {
constructor(parameters = {}) {
if (AnotherModel.pool[parameters.id] instanceof AnotherModel) {
return AnotherModel.pool[parameters.id];
}
AnotherModel.pool[parameters.id]
this.id = parameters.id;
this.models = [];
//...
}
}
Object.defineProperty(AnotherModel, 'pool', {
value: {}
});
As added by #Vardius, one can also create a pseudo-abstract class (as JS does not have abstract classes) which can be extended from. Using new.target.name, a namespace within the pool of the abstract class can be created:
class Entity {
constructor(parameters = {}) {
if (Entity.pool[this.constructor.name] && Entity.pool[this.constructor.name][parameters.id] instanceof Entity) {
return Entity.pool[new.target.name][parameters.id];
}
Entity.pool[new.target.name][parameters.id] = this;
}
}
Object.defineProperty(Entity, 'pool', {value: {} });
I'm looking for a method to create nested js-objects of the same type with property-fallbacking.
I'd like to be able to write for instance:
state.isLoggedIn and if this object doesn't have this property (undefined) then it should look in a base-state etc until no base-states exists anymore, then return undefined.
With base-state I mean some other state that this state is based on, not inherited like is class inheritance.
I was thinking of making some kind of class like this:
function State(base) {
this.base = base; // also a state
}
When I try to get a property P from a state A that is based on another state B, and state A doesn't define a property P it should go look in state B instead.
I know I could use a function like state.getState(key) that looks in its own properties first and then in the base-properties. But I'm looking for a method of doing this with normal properties instead.
In C# it would look something like this (and making it a dynamic object I would get almost excatly the same ducked typed state I'm looking for in javascript):
class State
{
public State(State base)
{
_base = base;
}
State _base;
Dictionary<string, object> _values = new Dictionary<string, object>();
public object this[string key]
{
get { return _values.ContainsKey(key) ? _values[key] : _base[key]; }
set { _values[key] = value; }
}
}
Any ideas? Possible?
UPDATE:
This is what I got now:
function State() {
}
function ViewModelBase() {
var self = this;
self.__parent = ko.observable(null);
self.state = new State();
self.parent = ko.computed({
read: function () {
return self.__parent();
},
write: function (value) {
if (getObjectClass(value) !== "ViewModelBase") throw new Error("Wrong type of parent.");
var p = self.__parent();
if (p != null) throw new Error("Allready parented.");
self.__parent(value);
// here i'd like to inherit/nest self.state with value.state
}
});
}
Not sure if this is what you're looking for, but maybe it is:
var state1 = {a : 1};
var state2 = Object.create(state1);
state2.b = 2;
console.log(state2.a); // 1
var state3 = Object.create(state2);
state3.a = 10; // creates an own "a" in state3
console.log(state1.a); // 1
console.log(state2.a); // 1
console.log(state3.b); // 2
This is using inheritance, as I suggested in my original comment to your question. Object.create returns a new object that uses the object passed as the first argument as its [[Prototype]] (which some implementations expose via the __proto__ property). When you try to access a property of the new object and an own property is not found, it looks up in the prototype chain.
Object.create is not supported by older browsers, but a very simple polyfill is available on MDN.
This is what CoffeeScript uses for class extenstions (using prototype inheritance):
var __hasProp = {}.hasOwnProperty,
__extends = function (child, parent) {
for (var key in parent) {
if (__hasProp.call(parent, key)) child[key] = parent[key];
}
function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
};
This assumes that the class functions are all a part of the prototype of the class functions.
For example:
Animal = (function() {
function Animal() {}
Animal.prototype.name = 'Generic Animal';
Animal.prototype.my_sound = 'none';
Animal.prototype.sound = function() {
return console.log(this.my_sound);
};
return Animal;
})();
Cow = (function(_super) {
__extends(Cow, _super);
function Cow() {
return Cow.__super__.constructor.apply(this, arguments);
}
Cow.prototype.name = 'Cow';
Cow.prototype.my_sound = 'Moo';
return Cow;
})(Animal);
Cat = (function(_super) {
__extends(Cat, _super);
function Cat() {
return Cat.__super__.constructor.apply(this, arguments);
}
Cat.prototype.name = 'Cat';
Cat.prototype.my_sound = 'Meow';
return Cat;
})(Animal);
Is it possible to define an object within another object? I'm thinking something like this:
function MyObj(name) {
this.name = name;
function EmbeddedObj(id) {
this.id = id;
}
}
And I could then create an EmbeddedObj like this:
var myEmbeddedObj = new MyObj.EmbeddedObj();
Meme for bonus points: Objectception! :o
Yes, and no.
function MyObj(name) {
this.name = name;
}
MyObj.EmbeddedObj = function EmbeddedObj(id) {
this.id = id;
}
new MyObj.EmbeddedObj(42);
Would run, but it might not yield the expected results for "embedded object" (see comment).
Note that in the case of new expr the expression is evaluated first so, in this case it creates a new object using the function-object evaluated from MyObject.EmbeddedObj as a constructor. (There is a silly rule with parenthesis in the expression, but that's another story.)
Now, if a "parent" and "child" relationship was desired, that could be done, using a more round-about method:
function Parent (name) {
this.name = name;
var parent = this; // for closure
this.Child = function Child () {
this.Parent = parent;
}
}
// create new parent object
var parent = new Parent();
// each new parent has a different Child constructor and
// any function-object can be used as a constructor
var child = new parent.Child();
// true: child is "bound" to parent
child.Parent === parent;
function MyObj(name) {
this.name = name;
}
MyObj.EmbeddedObj = function(id) {
this.id = id;
}
var myEmbeddedObj = new MyObj.EmbeddedObj();
Does that look like what you're after?
Here is example of nested constructor.
function cimdb(name,review,year) {
function nestedConstructor(name,review,year) {
this.name = name;
this.review = review;
this.year = year
};
this.name = name;
this[name] = new nestedConstructor(name,review,year);
}
var lionking = new cimdb("The Lion King", "The lion King review ..", 2015);
I guess this is what you mean by nested object constructor.
The easiest way to nest other objects in a constructor is to create its field and then create a new object when invoking the constructor. Below is an example:
function Product(name, price, category, producer) {
this.name = name;
this.price = price;
this.category = category;
// nested constructor
this.producer = producer;
}
function Producer(contributor, address) {
this.contributor = contributor;
this.address = address;
}
let prod1 = new Product("Milk", 2.5, "Dairy", new Producer("Nestle", "Warszawa"));