Referring to parent object from the child - javascript

class Test {
constructor() {
this.childObject = new Child();
}
parentHello() {
console.log("Parent Hello World!");
}
}
class Child {
childHello() {
// How do I call parentHello() from here?
}
}
const obj = new Test();
obj.childObject.childHello();
I'm trying to call the parentHello() from the childHello(). The only way I came up with is to change the structure to circular like this:
class Test {
constructor() {
this.childObject = new Child(this);
}
parentHello() {
console.log("Parent Hello World!");
}
}
class Child {
constructor(parent) {
this.parent = parent;
}
childHello() {
this.parent.parentHello();
}
}
const obj = new Test();
obj.childObject.childHello();
But after doing this I'm no longer able to convert it to JSON. Is there a proper way to do this?
EDIT: I also tried super() but it only works when extending.

Your solution with reference to parent via constructor is ok I think. To allow serialization to JSON, you can use second parameter of JSON.stringify (replacer):
A function that alters the behavior of the stringification process, or an array of String and Number objects that serve as a whitelist for selecting/filtering the properties of the value object to be included in the JSON string. If this value is null or not provided, all properties of the object are included in the resulting JSON string.
class Test {
constructor() {
this.childObject = new Child(this);
}
parentHello() {
console.log("Parent Hello World!");
}
}
class Child {
constructor(parent) {
this.parent = parent;
}
childHello() {
this.parent.parentHello();
}
}
const obj = new Test();
var cache = [];
var json = JSON.stringify(obj, (key, value) => {
if (typeof value === 'object' && value !== null) {
if (cache.includes(value)) {
return;
}
cache.push(value);
}
return value;
});
console.log(json);

Related

call a methods of a class without using NEW keyword inside other class node js

I want to access Main class methods to another Person class without creating a new instance Is it possible??
Can we access it without creating an instance of a class
let myInstance = new Person();
class Main {
constructor(args) {
this.hooks = [];
}
add_hooks(name, func) {
if (!this.hooks[name]) this.hooks[name] = [];
this.hooks[name].push(func);
}
call_hooks(name, ...params) {
if (this.hooks[name]) this.hooks[name].forEach((func) => func(...params));
}
}
other class Person how to access without using new keyword
const Main = require("./main.js");
class Person {
exec() {
const action = Main();
action.add_hook("jump", console.log.bind(console, "this will log "));
}
}
There is no big magic to it. Since the OP just wants to reuse prototypal Main methods, one is going to explicitly delegate the method/s of interest which was/were provided/accessed before via Main.prototype ...
class Main {
constructor(args) {
this.hooks = {};
}
add_hooks(name, func) {
if (!this.hooks[name]) {
this.hooks[name] = [];
}
this.hooks[name].push(func);
}
call_hooks(name, ...params) {
if (this.hooks[name]) {
this.hooks[name].forEach(func => func(...params));
}
}
}
// const Main = require("./main.js");
class Person {
// // ... either add `hooks` as public property at instantiation time ...
// hooks = {};
exec() {
const ref = Main.prototype;
ref.add_hooks.call(this, "jump", console.log.bind(console, "this will log"));
}
}
// ... or add `hooks` via additional glue code ...
function createPersonWithHooksAndExecute() {
const type = new Person();
type.hooks = {};
type.exec();
return type;
}
const someone = createPersonWithHooksAndExecute();
console.log({ someone });
// this will log
Main.prototype.call_hooks.call(someone, "jump");
.as-console-wrapper { min-height: 100%!important; top: 0; }
If you're not planning on instantiating the object, and you don't care about having multiple instances with each having their own state, you don't need a class.
Just create individual functions, or export an object.
const hooks = [];
export function add_hooks(name, func) {
if (!hooks[name]) hooks[name] = [];
hooks[name].push(func);
}
export function call_hooks(name, ...params) {
if (!hooks[name]) return;
for (const func of this.hooks[name]) {
func(...params);
}
}
It's possible too to do this with static methods, and that would be the likely answer if you write Java where everything has to be a class, but I wouldn't recommended it in Javascript.

Make object method a variable in vanilla JS

Say, I have a constructor called Animal, that has two prototype methods, foo and bar.
I create a new object:
const animal = new Animal()
I could call:
animal.foo()
or
animal.bar()
Can I replace the two methods with another method (baz)?
Like:
animal.baz()
So depending on the context, I want to assign either foo or bar to baz.
The object-oriented way to do this would be to create two subclasses, one for each context. In each one, you would define the baz method to simply pass its arguments on to this.foo or this.bar, respectively.
You could also dynamically define baz on objects using animal.baz = Animal.<foo|bar>. However, that strategy would be less maintainable (you would have to define that every time you instantiated an object), and less performant (interpreters are optimized for objects with unchanging properties).
Without knowing what form the context is in, a simple way you could do this would be to pass the context (in whatever form) in the constructor, store it in the object, and when baz is called, check the stored context and act accordingly. This is also pretty flexible:
// ES6
class Animal {
constructor(context) {
this.context = context;
}
foo() {
...
}
bar() {
...
}
baz() {
if(context == "some string id maybe?") {
return this.foo();
} else if(context == 13523) { // some integer id
return this.foo();
} else if(context === someContextObject) { // some context object
return this.bar();
} else {
return this.foo();
}
}
}
//Vanilla
const Animal = function(context) {
this.context = context;
}
Animal.prototype.foo = function() {
...
}
Animal.prototype.bar = function() {
...
}
Animal.prototype.baz = function() {
if(context == "some string id maybe?") {
return this.foo();
} else if(context == 13523) { // some integer id
return this.foo();
} else if(context === someContextObject) { // some context object
return this.bar();
} else {
return this.foo();
}
}
Something like this? :D
class Animal {
constructor(isAgressive) {
this.seesMe = isAgressive ? this.attack : this.flee;
}
attack = function () {
console.log('attack!');
}
flee() {
console.log('cya!');
}
}
const zebra = new Animal(false);
const lion = new Animal(true);
zebra.seesMe();
lion.seesMe();

JSON stringify ES6 class property with getter/setter

I have a JavaScript ES6 class that has a property set with set and accessed with get functions. It is also a constructor parameter so the class can be instantiated with said property.
class MyClass {
constructor(property) {
this.property = property
}
set property(prop) {
// Some validation etc.
this._property = prop
}
get property() {
return this._property
}
}
I use _property to escape the JS gotcha of using get/set that results in an infinite loop if I set directly to property.
Now I need to stringify an instance of MyClass to send it with a HTTP request. The stringified JSON is an object like:
{
//...
_property:
}
I need the resulting JSON string to preserve property so the service I am sending it to can parse it correctly. I also need property to remain in the constructor because I need to construct instances of MyClass from JSON sent by the service (which is sending objects with property not _property).
How do I get around this? Should I just intercept the MyClass instance before sending it to the HTTP request and mutate _property to property using regex? This seems ugly, but I will be able to keep my current code.
Alternatively I can intercept the JSON being sent to the client from the service and instantiate MyClass with a totally different property name. However this means a different representation of the class either side of the service.
You can use toJSON method to customise the way your class serialises to JSON:
class MyClass {
constructor(property) {
this.property = property
}
set property(prop) {
// Some validation etc.
this._property = prop
}
get property() {
return this._property
}
toJSON() {
return {
property: this.property
}
}
}
If you want to avoid calling toJson, there is another solution using enumerable and writable:
class MyClass {
constructor(property) {
Object.defineProperties(this, {
_property: {writable: true, enumerable: false},
property: {
get: function () { return this._property; },
set: function (property) { this._property = property; },
enumerable: true
}
});
this.property = property;
}
}
I made some adjustments to the script of Alon Bar. Below is a version of the script that works perfectly for me.
toJSON() {
const jsonObj = Object.assign({}, this);
const proto = Object.getPrototypeOf(this);
for (const key of Object.getOwnPropertyNames(proto)) {
const desc = Object.getOwnPropertyDescriptor(proto, key);
const hasGetter = desc && typeof desc.get === 'function';
if (hasGetter) {
jsonObj[key] = this[key];
}
}
return jsonObj;
}
As mentioned by #Amadan you can write your own toJSON method.
Further more, in order to avoid re-updating your method every time you add a property to your class you can use a more generic toJSON implementation.
class MyClass {
get prop1() {
return 'hello';
}
get prop2() {
return 'world';
}
toJSON() {
// start with an empty object (see other alternatives below)
const jsonObj = {};
// add all properties
const proto = Object.getPrototypeOf(this);
for (const key of Object.getOwnPropertyNames(proto)) {
const desc = Object.getOwnPropertyDescriptor(proto, key);
const hasGetter = desc && typeof desc.get === 'function';
if (hasGetter) {
jsonObj[key] = desc.get();
}
}
return jsonObj;
}
}
const instance = new MyClass();
const json = JSON.stringify(instance);
console.log(json); // outputs: {"prop1":"hello","prop2":"world"}
If you want to emit all properties and all fields you can replace const jsonObj = {}; with
const jsonObj = Object.assign({}, this);
Alternatively, if you want to emit all properties and some specific fields you can replace it with
const jsonObj = {
myField: myOtherField
};
Use private fields for internal use.
class PrivateClassFieldTest {
#property;
constructor(value) {
this.property = value;
}
get property() {
return this.#property;
}
set property(value) {
this.#property = value;
}
}
class Test {
constructor(value) {
this.property = value;
}
get property() {
return this._property;
}
set property(value) {
this._property = value;
}
}
class PublicClassFieldTest {
_property;
constructor(value) {
this.property = value;
}
get property() {
return this.property;
}
set property(value) {
this._property = value;
}
}
class PrivateClassFieldTest {
#property;
constructor(value) {
this.property = value;
}
get property() {
return this.#property;
}
set property(value) {
this.#property = value;
}
}
console.log(JSON.stringify(new Test("test")));
console.log(JSON.stringify(new PublicClassFieldTest("test")));
console.log(JSON.stringify(new PrivateClassFieldTest("test")));
I've made an npm module named esserializer to solve such problem: stringify an instance of JavaScript class, so that it can be sent with HTTP request:
// Client side
const ESSerializer = require('esserializer');
const serializedText = ESSerializer.serialize(anInstanceOfMyClass);
// Send HTTP request, with serializedText as data
On service side, use esserializer again to deserialize the data into a perfect copy of anInstanceOfMyClass, with all getter/setter fields (such as property) retained:
// Node.js service side
const deserializedObj = ESSerializer.deserialize(serializedText, [MyClass]);
// deserializedObj is a perfect copy of anInstanceOfMyClass
I ran into the same issue but I have no access to the class construction and I'm not able to add or override the ToJson method
here is the solution that helped me solve it
a simple class with getters and properties
class MyClass {
jack = "yoo"
get prop1() {
return 'hello';
}
get prop2() {
return 'world';
}
}
a class with a child class and also child object with getters
class MyClassB {
constructor() {
this.otherClass = new MyClass()
}
joe = "yoo"
otherObject = {
youplaboum: "yoo",
get propOtherObject() {
return 'propOtherObjectValue';
}
}
get prop1() {
return 'helloClassB';
}
get prop2() {
return 'worldClassB';
}
}
here is the magic recursive function inspired by the ToJSON made by #bits
const objectWithGetters = function (instance) {
const jsonObj = Object.assign({}, instance);
const proto = Object.getPrototypeOf(instance);
for (const key of Object.getOwnPropertyNames(proto)) {
const desc = Object.getOwnPropertyDescriptor(proto, key);
const hasGetter = desc && typeof desc.get === 'function';
if (hasGetter) {
jsonObj[key] = desc.get();
}
}
for (let i in jsonObj) {
let value = jsonObj[i];
if (typeof value === "object" && value.constructor) {
jsonObj[i] = objectWithGetters(value);
}
}
return jsonObj;
}
const instance = new MyClassB();
const jsonObj = objectWithGetters(instance)
console.log(jsonObj)
let json = JSON.parse(jsonObj);
console.log(json)

How to handle class object with circular references?

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: {} });

A JavaScript Framework for Instance/Static Variables?

Here is the current method I use, where everything is either all public or private. However I would like to differentiate between static and instance. How would I add this in?
obj holds the class or object have you, and config_module determines what type of module this is.
All private, All public ( a collection of statics ), or instance based on a constructor method.
If I do use an instance based, how do I differentiate between statics and instance based properties?
$P.support = $P.parsel = function (obj, config_module) {
$R.Parsel[obj.Name] = obj;
// all properties are private
if (!config_module) {
return undefined;
}
// all properties are public
if (config_module === true) {
return obj;
}
// constructor based, all properties are public
if (config_module === 'constructor') {
var object_public;
if (obj.constructor) {
object_public = obj.constructor;
delete obj.constructor;
}
$A.someKey(obj, function (val, key) {
// like this ?
if (/^s_/.test(key)) {
object_public[key] = val;
// like this ?
} else if (/^p_/.test(key)) {
object_public.prototype[key] = val;
} else {
object_public.prototype[key] = val;
}
});
return object_public;
}
};
You can have (pseudo) static stuff by adding properties to the constructor:
function Something(){}
Something.getStaticFoo = function(){ return 'foo'; }
Something.getStaticFoo();
var instance = new Something();
instance.getStaticFoo(); // error
If I understand your code, that's the same as object_public[key] = val;.

Categories