Js using reflection to create subclass in parentclass method - javascript

I got this idea about js extends system. It'll be much easier to understand what i mean by reading the following codes
in moduleA.js I have Frame class
class Frame{ // contains frame info
static Predefined_Paramters_Pattern = [1,2,3,4]
constructor(someParameters = {variable1=...,variable2=...,tag = 0}){
//do something
this.someParamtersPattern = someParamters
}
nextFrame(){
// for example
// when i call nextFrame it 'll construct 4 subclasses
// base on [this] constructor paramaters
// constructorParmatersPattern = {variable1=...,variable2=...,tag= 1 or 2 or 3 or 4}
// this code may looks meaningless
// but i just wanna make this a lot easier to be comprehended what i mean
// and avoid stucking into a complex business logic
Frame.Predefined_Paramters_Pattern.forEach(e=>{
// create sub class somehow
let type = this.type
this.someParamtersPattern.tag = e
let constructorParmatersPattern = this.someParamtersPattern
// if i use eval(`new ${type}(${constructorParmatersPattern })`)
// the browser will throw a error looks like 'SubFrame1 is not define'
})
}
}
export {Frame}
in moduleB.js I have SubFrame1 class
import {Frame} from './moduleA.js'
class SubFrame1 extends Frame{
constructor(somePramaters){
//do something
super(somePramaters)
//do something
this.type = 'SubFrame1'
}
}
export {SubFrame1}
Now what I want to achieve is when i call new SubFrame1().nextFrame(), it'll create several SubFrames automaticlly which are similar to the one I created. I can define some patterns for their constructor's paramaters. So no matter how many subclasses i have, as long as they extends Frame, then they all share the same patterns when they call .nextFrame().
But the thing is I do not wish to have a SubFrame1 class or SubFrame1,SubFrame2,...,SubFrame3 classes 's reference in my original class.
like this one
import {SubFrame1} from './subframe1.js'
import {SubFrame2} from './subframe2.js'
......
import {SubFrameN} from './subframen.js'
class Frame{
......
}
i'm pretty much a rookie to javascript but at school i was tought and told to write codes so I can reuse them and reduce redundant codes, any help or corrections would be great

So you want new SubFrame1().nextFrame() to construct a SubFrame1 instance, but new SubFrame2().nextFrame() to construct a SubFrame2 instance. Nothing easier than that, you don't even need a type property for that! JS prototype object (e.g. built with class syntax) already do contain a .constructor property holding the constructor function that you can call to create a new instance of the same (sub)class.
export class Frame{ // contains frame info
constructor(someParameters) {
// do something
this.someProperties = someParameters;
}
nextFrame() {
const type = this.constructor;
const instance = new type(this.someProperties);
return instance;
}
}
Now this assumes that the subclass constructors would be called with the same signature, and always expect the this.someProperties object as arguments for the cloning of the frame. To customise this, the subclass could of course modify this.someProperties, but as soon as the signatures become too different the subclass should better override the whole method:
class SpecialFrame extends Frame {
constructor(someParameters, someSpecials) {
super(someParameters);
this.specialProperties = someSpecials;
}
nextFrame() {
return new this.constructor(this.someProperties, this.specialProperties);
}
}
This can also be used for more complex initialisation than just copying a property value, or for hardcoding some specific arguments that nextFrame should always use. If nextFrame does more than just the cloning, split it into two methods - clone() that creates the copy and may be overridden in the subclasses, and nextFrame that calls this.clone() then modifies the copy to become the next frame instead of the same.

Edit : Bergi offered a much better solution!
____________________________________________________________________
I find out how i do this with es6 Reflect API in this link below
Create instance of "Class" Using "Reflection" in JavaScript
posted by soulshined
in moduleA.js
class Frame{ // contains frame info
static Predefined_Paramters_Pattern = [1,2,3,4]
constructor(someParameters = {variable1=...,variable2=...,tag = 0}){
//do something
this.someParamtersPattern = someParamters
}
nextFrame(){
Frame.Predefined_Paramters_Pattern.forEach(e=>{
// create sub class
let type = this.type
this.someParamtersPattern.tag = e
let constructorParmatersPattern = this.someParamtersPattern
Reflect.construct(this.type, constructorParmatersPattern )
})
}
}
export {Frame}
and subclass
import {Frame} from './moduleA.js'
class SubFrame1 extends Frame{
constructor(somePramaters){
//do something
super(somePramaters)
//do something
this.type = SubFrame1
}
}
export {SubFrame1}

Related

Modules vs Class memory difference

I have some methods and properties that I would like to be available to other files in my JS application.
I see I have 2 options, I can either have a class:
class MusicTile {
artistTitle = find('someProperty')
play() {
// Some logic
}
}
And then do let myTile = new MusicTiles
Or just export like so
let artistTitle = find('someProperty')
function play() {
// Some logic
}
export { play }
And just import { * } from './musicTile.js'
I'm curious which of the 2 is better to use. I don't really need to instantiate MusicTile, I just want the properties from there so I guess a plain old export is fine. But is there something else to consider?
Also how is memory usage between the two, I guess the export doesn't do anything unless I call the methods in there. But the class approach (at least how it is with the artistTitle = find('someProperty') outside a method) would call find() as soon as I do new MusicTile so consume more memory?

Prevent TypeScript public function calling a private function

I have this class:
export class ResourceFactory {
AlgoliaAppId = "AlgoliaAppId";
// ...
private resetParams() {
this.AlgoliaAppId = "AlgoliaAppId";
// ...
}
public initTemplate(objectName, directiveArgs): Template {
this.resetParams(); // <-- Can I, in any possible way, prevent this line from running?
this.AlgoliaAppId = `${this.AlgoliaAppId}${objectName}`;
// ... [long function content which I don't want to duplicate]
}
}
I'm trying to extend the ResourceFactory class: I want to change the AlgoliaAppId name and prevent resetParams from running. (I cannot edit the original class).
Is there any way to override resetParams even though it's private, or at least somehow monkey-patch the initTemplate method so it won't run the line this.resetParams?
There's no (clean) way to override a private method from a base class you don't have control over. I tried some module augmentation to see if I could change the modifier to protected but didn't have much luck; TypeScript seems to want all overloads to have the same modifier.
However, I was able to patch the prototype after the class declaration to hack in an overload, at the cost of including a #ts-expect-error comment. Seems to be the path of least resistance for fighting with the compiler. Here's what an example subclass would look like:
class Example extends ResourceFactory {
AlgoliaAppId = "CustomName";
}
// #ts-expect-error
Example.prototype.resetParams = function resetParams() {
// your implementation
}
Here's a link to the playground as well.

How to check which module called the constructor of a class in Javascript at runtime?

I have a class:
// MyClass.ts
export class MyClass {
constructor(){
// can I get the name of the module or function which called the constructor here ??
// ex: SomeModule or testFunction
}
}
and here I create a new instance:
// SomeModule.ts
const testFunction = () => {
return new MyClass();
}
I don't want to have to pass an extra parameter to the constructor to indicate who created it. I want to know inside the constructor the module or the function in which a new instance of the MyClass was created.
I want to know inside the constructor the module or the function in which a new instance of the MyClass was created.
That's not possible. And you shouldn't need to know anyway. If it actually is important, you should be passing it as an explicit argument.
It's mostly for logging purposes.
For that use case, you might find that a stack trace is enough (see Print current stack trace in JavaScript and related topics). A better practice though is to pass a logger object that contains the relevant context information and uses it to enrich the log calls.

ES6: Instantiate class without calling constructor

Is there any way how to instantiate new class instance without calling its constructor?
Something like this:
class Test {
constructor(foo) {
this.foo = 'test';
}
}
const a = new Test('bar'); // call constructor
const b = Test.create(); // do not call constructor
console.log(a.foo, a instanceof Test); // bar, true
console.log(b.foo, b instanceof Test); // undefined, true
I am trying to develop TS mongo ORM, and would like to use constructors of entities for creating new objects, but do not want to call them when instancing entities of already persisted objects (those that are already stored in DB).
I know that doctrine (PHP ORM) uses this approach, but afaik they are using proxy classes to achieve it. Is there any easy way to achieve this in typescript (or generally in ES6/ES7)?
I already found this question ES6: call class constructor without new keyword, that asks for the opposite, and saw one answer mentioning Proxy object. That sounds like a possible way to go, but from the docs I am not really sure if it is achievable.
You can add a static method create, that create an Object from the class prototype. Something like that should work:
class Test {
constructor(foo) {
this.foo = foo
}
static create() {
return Object.create(this.prototype)
}
}
const a = new Test('bar') // call constructor
const b = Test.create() // do not call constructor
console.log(a.foo, a instanceof Test) // bar, true
console.log(b.foo, b instanceof Test) // undefined, true

javascript: dynamically create class instances

I am writing an app that has features that can be turned on and off via a config.json that looks something like this:
"appFeatures": {
"header": {
"create": true,
"title": "Here Comes the Sun"
},
"imageStrip": {
"create": false,
"imageDirectory": "../data/images",
"imageDimensions": [288, 162]
},
"areaChart": {
"create": true
},
"axes": {
"create": true
}
}
For each feature there is already a corresponding class of the same name that implements the feature. I'd like to use the name of the feature to create a new instance of the class. After fetching the config.json, I have code (within a Main.js class) that looks like:
this.features = Object.entries(this.config.appFeatures)
.filter((entry) => {
return entry[1].create === true;
});
this.features.forEach((feature) => { this.createFeatureInstances(feature[0]); });
And then I try to create instances, a la this.header = new Header():
createFeatureInstances(featureName) {
const className = `${featureName.replace(featureName[0], featureName[0].toUpperCase())}`;
this[`${featureName}`] = new Function(`
return class ${className} {}
`)();
This creates a new, empty Header class and, I suppose, it's instance. It is not the Header class that I have already written and want to create an instance for. How might I write the createFeatureInstances function so that I can create the instance of each class that corresponds to a feature?
EDIT Because new features may be added to this app in the future by others, I would like to minimize the times that I hard code which features are available to the app. With my current design, another developer can add another feature by writing a new feature class, importing that class into the Main.js class, and pop the config entries into the config .json without having to touch anything else in the code. For this reason, solutions like this one: Create an instance of a class in ES6 with a dynamic name? won't give me a solution because they rely on having a complete list of the classes that should already exist.
You need to have a name-class mapping somewhere. Factory function or somewhere else is your call. Your current solution lacks this, which is causing the problem:
...creates a new, empty Header class and, I suppose, it's instance. It is not the Header class that I have already written and want to create an instance for
Some explanation with a simple example
// Imagine you've defined Test
class Test {
callMe() {}
}
// And your consumer wants a className Test to be created
const className = "Test";
// This is a simple map of the name to class
const nameToClass = {
"Test": Test
}
// This creates a new class called Test, not your defined class
const AttemptedDynamicTest = new Function(`
return class ${className} {}
`)();
// The next two logs just prove the previous comment
console.log(Test === AttemptedDynamicTest); // false
console.log(typeof AttemptedDynamicTest.prototype.callMe); // undefined
// What you really need is to maintain a map, and just use it to dynamically
// create a new instance every time
console.log(typeof nameToClass[className].prototype.callMe); // function
You can use a string to initialize a cooresponding (valid and existing) class using the following snippet:
var dynamicClassInstance = new this[classNameString]();

Categories

Resources