Instantiating a class object with window[string]() [duplicate] - javascript

This question already has answers here:
Javascript ES6 class definition not accessible in window global
(2 answers)
Closed 3 years ago.
class Unit {
constructor(){
}
}
var str = "Unit";
var a = new window[str](); // error
var b = new window["Unit"](); // error
var u = new Unit(); // works
(u instanceof Unit) // true
I only recently made the switch to ES 6 syntax in regards to declaring classes. Im pretty sure that formerly i could instantiate a class like this, however ever since i have used "class" syntax, the instantiation of an object by windowclassName is not working anymore.
What exactly am i missing here ?

Variables declared with class behave similarly to those declared with const and let - they do not get implicitly assigned to the window object (which is arguably a good thing). If you wanted to put Unit on window, you would have to do so explicitly:
class Unit {
constructor(){
console.log('constructing');
}
}
window.Unit = Unit;
var str = "Unit";
var a = new window[str]();
You might consider using your own object of classes to avoid global pollution:
const myClasses = {
Unit: class Unit {
constructor(){
console.log('constructing');
}
}
};
var str = "Unit";
var a = new myClasses[str]();
Object values cannot reference each other while in the process of declaring an object literal - to put a subclass on myClasses that extends one of the existing classes, you'll have to do so outside of the myClasses declaration:
const myClasses = {
Unit: class Unit {
constructor(){
console.log('constructing');
}
}
};
myClasses.Child = class Child extends myClasses.Unit {
constructor() {
super();
console.log('Child running');
}
}
var str = "Child";
var a = new myClasses[str]();

Related

Listing all possible properties of a class in javascript [duplicate]

This question already has answers here:
Get properties of a class
(10 answers)
Closed 8 months ago.
I want to list all possible class properties. Something like this:
class Rectangle {
height;
width;
constructor(width) {
this.width = width;
}
}
getClassProperties(Rectangle) // ['height', 'width']
I want to be able to make this calculation give the class, not an instance of it. using hasOwnProperty \ getOwnPropertyName + getPrototypeOf will only work on an instance.
ES6 classes are a "syntactic sugar" method and are essentially functions. In order to get the list of properties you have to run the function i.e. instantiate the object.
class Rectangle {
height;
width;
constructor(width) {
this.width = width;
}
}
function getClassProperties(jsClass) {
if (typeof jsClass !== "function") {
throw Error("not a class");
}
return Object.keys(new jsClass);
}
console.log(getClassProperties(Rectangle));
Modified answer from: https://stackoverflow.com/a/46237515/17954209
A general solution will be:
class A {
private a1;
private a2;
constructor(a1:number, a2:string){
this.a1 = a1;
this.a2 = a2;
}
}
class Describer{
describeClass(typeOfClass: any){
let a = new typeOfClass();
let array = Object.getOwnPropertyNames(a);
return array;//you can apply any filter here
}
}
Or alternatively in a function form
function getClassProperties(typeOfClass: any) {
let a = new typeOfClass();
let array = Object.getOwnPropertyNames(a);
return array; //you can apply any filter here
}
// You tagged typescript in the question, but javascript in the actual title,
// so here's the JS equivalent
function getClassProperties(typeOfClass) {
let a = new typeOfClass();
let array = Object.getOwnPropertyNames(a);
return array; //you can apply any filter here
}
Basically at some point you'll have to create an instance of the class in order to run the constructor function to initialize the properties. You either have to initialize the property in the constructor, or while declaring it. Otherwise it will not work. It's explained well in the linked answer, quoted below. If you really don't define the property in the constructor but would like a workaround, simply set it to undefined or null in the constructor/declaration.
In Typescript
class A {
private a1;
private a2;
}
Generates the following code in Javascript:
var A = /** #class */ (function () {
function A() {
}
return A; }());
I would recommend reading more of the answer I modified from if you have any other questions.
If you want Typescript to give a return type as well you can use the handy ConstructorParameters utility type
function getClassProperties<T extends {new (...args: any): any}>(typeOfClass: T) {
let a = new typeOfClass();
let array = Object.getOwnPropertyNames(a);
return array as ConstructorParameters<T>;
}
View on TS Playground

Use overwritten value in parent constructor

I'm invoking a function in constructor where I want to use the variable from child in the function call that's made through parent constructor. For demo purpose I've created a small script to show what I want to do:
class A{
intVal = 1;
constructor() {
this.identifyClass();
}
identifyClass()
{
console.log("I am class A", this.intVal); // This should be the value that B has overwitten
}
}
class B extends A
{
intVal = 2;
}
const x = new B();
So I'd want that the function in parent constructor should use the value that was overwritten by B ie. intVal = 2 but currently it uses the original value. Can I use any workaround for this so that I don't have to create a constructor in B and then invoke the function there?
Note: It's a very complex app where I don't want breaking changes to A which is being used at a lot of places and the Class B is being exposed to public so I don't want the people using Class B to change anything if possible where currently they just overwrite the instance variable
Ok, I think I got it. We create a symbol, if the last argument passed to the constructor is not the symbol, then we create a new object, using this.constructor, passing in the original arguments, plus the symbol, and then call identify. This effectively separates the call to identify from the constructor, allowing us to get this.intVal from the subclass, while still being transparent to the user of the subclass.
const _sym = Symbol();
class A {
intVal = 1;
constructor() {
const args = Array.from(arguments);
const _ = args[args.length - 1];
if (_ !== _sym) {
args.push(_sym);
const el = new this.constructor(...args);
el.identify();
return el;
}
}
identify() {
console.log(this.intVal);
}
}
class B extends A {
intVal = 2;
}
const x = new A();
const y = new B();

How do we get access to a private variable in ES6 class?

Below we define a private variable boy for a would-be instance of the class 'Forrest'. By doing this JavaScript signals of no mistakes. But neither the instance, nor Forrest.prototype or Forrest function object itself show no signs of hosting this variable.
class Forrest {
constructor() {
let boy = "Bobby";
girl: "Marry";
}
}
const f = new Forrest();
However, we can easily get access to this private boy variable via vanilla JS constructor function.
function Forrest() {
let boy = "Bobby";
this.getBoy = function() {
console.log(boy);
}
}
const f = new Forrest();
f.getBoy(); // Bobby
How do we get access to this private (local) variable in ES6 class?
let boy is a variable only valid within the constructor's scope
girl: "Marry" is not a property at all
This is how you initialize and access attributes
class Forrest {
constructor() {
this.boy = "Bobby"
this.girl = "Marry"
}
}
const f = new Forrest();
console.log(f.boy)
console.log(f.girl)
// The answer turns out to be:
class Forrest {
constructor() {
let boy = "Bobby"
this.getBoy = function() { // This is it!
console.log(boy)
}
}
}
const f = new Forrest()
f.getBoy() // Bobby

Reference to class property is null

I was under the impression that anything that is part of an object is set by reference. This doesn't seem to be the case in this example
class System {}
class Foo {
constructor() {
this.system = null;
this.dictionary = {
system: this.system
}
}
}
class Bar extends Foo {
constructor() {
super();
this.system = new System();
}
}
var bar = new Bar();
console.log(bar.system); // System{}
console.log(bar.dictionary.system); // null
I would expect that dictionary is holding a reference to this.system which starts as null, but only because what it is referencing is null. However as can be seen, bar.dictionary.system is actually still null even though its reference was updated.
Can anyone explain what is happening here?
It's still being set by reference. The issue here is that by writing this.system = new System() you're not modifying the referenced value, you're making this.system reference a different value altogether. Meanwhile, this.dictionary.system is still pointing towards the old value.
Consider the following code:
class Foo {
constructor() {
this.system = {};
this.dictionary = { system: this.system };
}
}
class Bar {
constructor() {
super();
this.system.bananas = 5;
}
}
this would correctly add bananas to this.system and this.dictionary.system, as you're modifying the value being referenced, not making this.system reference a different value entirely.
You are trying to use a value of system, which is assigned, after you actually want to use it. That's like the follwing, which won't also work
var x = null;
console.log(x);
x = "Hallo Welt";
The problem in your case is, you couldn't simply swap the call of super() with the assignment - which would solve the problem. Instead you could use properties or functions, which are getting overridden in the child class and return the actual value.
class Foo {
constructor() {
this.dictionary = {
system: this.system
}
}
get system() { return null; }
}
class Bar extends Foo {
constructor() {
super();
}
get system() {
return this._system ?
this._system :
this._system = new System();
}
}
You are assigning this.system after you call the base constructor. If you do this, you don't update dictionary once you call this.system = new System();
Here is a rough imitation of what the compiler sees:
this.system = null;
this.dictionary = {
system: this.system
}
this.system = new System();
See how you are not actually updating this.dictionary.
Imagine I have this code:
var a = 5;
var b = a;
a = 6;
At this point, a is 6 and b is 5. This is because I set b to the previous value of a. If I want to update b, I have to equate them again.

JavaScript ES6 inherintance kills properties in classes?

This is example of very basic code:
"use strict";
class aClass {
readFromA() {
console.log(this.a);
}
constructor() {
this.a = 5;
}
}
class bClass extends aClass {
readFromB() {
console.log(this.a);
}
constructor() {
super();
this.a = 10;
}
}
let bc = new bClass();
bc.readFromA(); //10
bc.readFromB(); //10
My intention is to involve the most modern techniques of object programming in JS. ES6 introduces classes and inheritance of them. It seems to be useless programming style yet. For example, code above overrides property "a" in class aClass by the same variable name in bClass. .
Lets assume that 2 proggramers create those classes. Each of them doesn't know what variable names will be used. If they both use the same variable name - it will couse a catastrophy! Both classes will read and write the same property making application crash. How to protect properties in classes against overriding and be able to utilize "extends" functionality?
Each of them doesn't know what variable names will be used. If they both use the same variable name - it will cause a catastrophy!
This is of course a very bad practice. You should not inherit from classes that you don't know, and every class should document its public members for exactly this purpose.
How to protect properties in classes against overriding and be able to utilize "extends" functionality?
Don't use the same keys. If you cannot ensure this using proper documentation or naming conventions, symbols were made to solve exactly this problem.
const a = Symbol("a");
export default class {
constructor() {
this[a] = 5;
}
readFromA() {
console.log(this[a]);
}
}
import ClassA from '…';
const a = Symbol("a"); // a different symbol than that in the AClass module
class BClass extends AClass {
constructor() {
super();
this.a = 10;
this[a] = 15;
}
readFromB() {
console.log(this.a, this[a]);
}
}
const x = new BClass();
x.readFromA(); // 5
x.readFromB(); // 10, 15
In the meantime I found solution to mentioned problem of private properties in classes. Bergi's idea was implemented. Here is the code:
"use strict";
let aClass = (function () {
let a = Symbol("a");
class aClass {
readFromA() {
console.log(this[a], this.b);
}
constructor() {
this[a] = "aaa"; //this is private
this.b="bbb"; // this is public
}
}
return aClass;
})();
let bClass = (function () {
let a = Symbol("a");
class bClass extends aClass {
readFromB() {
console.log(this[a], this.b);
}
constructor() {
super();
this[a] = "ccc"; //this is private
this.b="ddd"; // this is public
}
}
return bClass;
})();
let bc=new bClass();
bc.readFromA();
bc.readFromB();
The result is:
aaa ddd
ccc ddd
Thanks to encapsulation I managed with using the same property name "a" in both classes which receives different values for each of them. Property "b" is public and can be overriden. With this approach, we do not have to be careful using property names in both classes. There is no risk of accidentally overwriting the non public properties of the base class.
Moreover, this kind of encapsulation allows to use inheritance of classes in traditional way: by "extends" keyword.

Categories