JavaScript How to get subclasses? [duplicate] - javascript

I have code like this
class Animal{}
class Dog extends Animal {}
class Cat extends Animal {}
class Donkey extends Animal {}
I want to look at all of the classes in my application's universe, and when I find one that descends from Animal, I want to create a new object of that type and add it to the list. This allows me to add functionality without having to update a list of things. So I can avoid the following:
var animals = [];
animals.push( new Dog() );
animals.push( new Cat() );
animals.push( new Donkey() );
PS: I don't want to add extra functionality to my classes or call them explicitly.

I think you could make use of decorators. For instance, you could create #Extends() one and provide base class as an argument, e.g. #Extends(Animal). Inside the decorator function, you could take the name of the class decorated with #Extends and and put it into an array or an object. Don't know if it is applicable in browsers, but it should be. In Node with TypeScript I would do something like:
import { MyClassMetadata } from './';
export function Extends(parent): (...args: any[]) => void {
return (target: object): void => {
MyClassMetadata.someVariableToStoreChildren[parent.constructor.name].push(
target,
);
}
}
Then you can access the MyClassMetadata variable that stores array of children of a given class and use it the way you want. You can play with it and get the desired result.

What about this:
class Animal {
static derived = new Set();
}
class Dog extends Animal {
static dummy = Animal.derived.add(this.name);
}
class Cat extends Animal {
static dummy = Animal.derived.add(this.name);
}
class Donkey extends Animal {
static dummy = Animal.derived.add(this.name);
}
console.log(Animal.derived);
I tried this in a TypeScript environment. The result:
Set(3) {"Dog", "Cat", "Donkey"}
without instantiating a class.

Here what I discovered so far
http://jsbin.com/xiroyurinu/1/edit?js,console,output
class Animal{}
class Dog extends Animal {}
class Cat extends Animal {}
class Donkey extends Animal {}
var animals = getAllSubclasses(Animal);
console.log(animals.map(function(c){ return new window[c] })) // creates objects
document.body.innerText = animals; // Dog, Cat, Donkey
and the magic
function getAllSubclasses(baseClass) {
var globalObject = Function('return this')();
var allVars = Object.keys(globalObject);
var classes = allVars.filter(function (key) {
try {
var obj = globalObject[key];
return obj.prototype instanceof baseClass;
} catch (e) {
return false;
}
});
return classes;
}
The main disadvantage of this method is that I can not use ES6 module import and have to do old fashioned and simple contatenation of files, but this is still better that nothing.
PS: still wait for better answer
UPD: and ye, i know that to use this all classes must be defined globally, that's why i search for better way to do this..

It is not possible. You can never know e.g. about local classes defined inside some function, or privately in another module. And that's by design. It would be unmodular and break encapsulation.
Also, the set of classes is not static in JavaScript. You can create new classes dynamically open-endedly.
If you think you want such functionality then I strongly suggest you're holding it wrong. What are you trying to achieve?

Related

How could one refactor the implementation of two different types of sub classes while keeping both, functionality and naming of their methods?

I have a single base abstract class that expects to be extended, which has defined a sole public method that uses a functionality expected from a subclass. Here, only fullSoundSentence is meant to be "public" - to be used by the outside world.
class Animal {
constructor(nickname){
this.nickname = nickname
}
fullSoundSentence(){
//expects this.composesound to exist
return `${this.nickname} ${this.composesound()}!`
}
}
I then have many classes extending it providing some core functionality (here they simply return a string but in reality they need access to this, modify properties of the object and so on)
class Dog extends Animal {
doessound(){
return "barks"
}
}
class Cat extends Animal {
doessound(){
return "meows"
}
}
Another family of subclasses then uses this functionality, all in a different way. Kind of
"modes" in which we use the Dog and Cat functionalities. Theese modes provide the function expected by the abstract base method (fullSoundSentence), but they need to be a specific animal.
class LoudAnimal extends Animal {
composesound(){
return `loudly ${this.doessound()}`
}
}
class CalmAnimal extends Animal {
composesound(){
return `calmly ${this.doessound()}`
}
}
Now, both cats and dogs can be both calm and loud. = any animal can be in any mode
Q: How do i create a calm dog or a loud cat and so on?
I could create by hand classes for each combination (CalmDog, LoudDog, CalmCat, ...) but if there are more animals and more modes, this is terrible
I could make the modes just object with functionalities that the abstract Animal would expect as an argument for example, but this is messy, especially if the mode also needs to modify properties and so on
Isn't there some better way?? I'm looking for ways to do this in JavaScript or TypeScript.
The next provided approach introduces a toolset of (a) context aware functions and function based mixins (which implement behavior that can not be provided in a meaningful way via a sub-typed class-hierarchy) and (b) an Animal base class, some more specific animal sub types and also a factory in order to show that mixin-based composition can be applied at any time during and at any level of an object creation process (or an object's lifetime).
All implemented functionality got derived from the OP's example code, decomposed and re-assembled while keeping the animal type specific property and method names.
From the OP's originally provided Animal and Dog/Cat extends Animal code, in order to justify the existence of a base Animal class, and also in order to DRY both subtypes (Dog and Cat), such a basic type has to not only feature its nickname but also its sound property and its accompanying doessound method. Dog and Cat both rely on super delegation and are good enough for just providing the default values.
The rest is up to either a mix of further sub typing and mixin composition or to e.g. a factory which does instantiate a class and does apply specific behavior to an instance via mixin composition as well.
Why is that?
Both code lines ...
class LoudAnimal extends Animal { composesound () { /* ... */ } }
... and ...
class CalmAnimal extends Animal { composesound () { /* ... */ } }
... make no sense at all.
A calm/loud animal type is nothing which should/could be described in a meaningful way like in form of an entity which features a nickname and a sound property and a doessound method whereas a dog/cat can be described and recognized by these animal specific keys.
But there can be loud dogs and/or calm cats and/or vice versa. Thus, such types need to acquire specific behavior/traits like being calm/loud or expressing itself calmly/loudly. And this is what mixins are for.
// sound-specific functions/methods (behavior) utilized by mixins.
function getBoundBehaviorDrivenSound(behavior) {
// expects `doessound` to exist but does
// handle it forgiving via optional chaining.
return `${ behavior } ${ this?.doessound?.() }`;
}
function getFullSound() {
// expects `nickname` and `composesound` to exist but
// handles the latter forgiving via optional chaining.
return `${ this.nickname } ${ this?.composesound?.() }!`;
}
// sound-specific function-based mixins (applicable behavior).
function withSoundingLoud() {
this.composesound =
getBoundBehaviorDrivenSound.bind(this, 'loudly');
return this;
}
function withSoundingCalm() {
this.composesound =
getBoundBehaviorDrivenSound.bind(this, 'calmly');
return this;
}
function withFullSound() {
this.fullSoundSentence = getFullSound;
return this;
}
// base-class and sub-typing
class Animal {
constructor({
nickname = 'beast',
sound = 'grmpf',
}) {
Object.assign(this, { nickname, sound });
}
doessound() {
return this.sound;
}
}
class Dog extends Animal {
constructor(nickname = 'Buster') {
super({ nickname, sound: 'barks' });
}
}
class Cat extends Animal {
constructor(nickname = 'Chloe') {
super({ nickname, sound: 'meows' });
}
}
// further sub-typing and mixin based composition.
class LoudDog extends Dog {
constructor(nickname) {
super(nickname);
// mixin at creation time at instance/object level.
withSoundingLoud.call(this);
// withFullSound.call(this);
}
}
// factory function featuring mixin based composition.
function createFullySoundingCalmCat(nickname) {
// mixin at creation time at instance/object level.
return withFullSound.call(
withSoundingCalm.call(
new Cat(nickname)
)
);
}
const smokey = createFullySoundingCalmCat('Smokey');
const cooper = new LoudDog('Cooper');
console.log({ smokey, cooper });
console.log('smokey.doessound() ...', smokey.doessound());
console.log('cooper.doessound() ...', cooper.doessound());
console.log('smokey.composesound() ...', smokey.composesound());
console.log('cooper.composesound() ...', cooper.composesound());
console.log('smokey.fullSoundSentence() ...', smokey.fullSoundSentence());
console.log('cooper?.fullSoundSentence?.() ...', cooper?.fullSoundSentence?.());
// ... one more ...
class FullySoundingLoudDog extends LoudDog {
constructor(nickname) {
super(nickname);
}
}
// prototype level aggregation / "class level mixin".
withFullSound.call(FullySoundingLoudDog.prototype);
const anotherDog = new FullySoundingLoudDog;
console.log({ anotherDog });
console.log('anotherDog.doessound() ...', anotherDog.doessound());
console.log('anotherDog.composesound() ...', anotherDog.composesound());
console.log('anotherDog.fullSoundSentence() ...', anotherDog.fullSoundSentence());
.as-console-wrapper { min-height: 100%!important; top: 0; }

Create Child Class From Parent (ES6 Classes in Javascript)

What I am trying to do
I am trying to create a child (sub) class by initiating a parent class with the child type as a parameter, and I am wondering how to do this.
For example, say I have the following starter code:
class Animal{
constructor(settings){
//parent value
this.name = settings.name;
}
//parent function
sayName(){
console.log(`My name is ${this.name}.`);
}
}
class Frog extends Animal{
constructor(settings){
super(settings);
//child-specific value
this.isTreeFrog = settings.isTreeFrog;
}
//child function
livesInTheForest(){
return this.isTreeFrog;
}
}
class Rabbit extends Animal{ [...] }
class Whale extends Animal{ [...] }
I want to be able to write:
let barry = new Animal({
animalType: "frog",
name: "Barry",
isTreeFrog: false
})
(rather than let barry = new Frog({name: "Barry", isTreeFrog: false}))
and have barry be a frog, meaning I can write things like this:
barry.sayName() //should print 'My name is Barry'
console.log(barry.livesInTheForest()) //should print 'false'
What I have Tried
I have tried two different ways to achieve this, but both are a bit hacky and don't achieve exactly what I want.
The first involves having a value in the Animal class which stores the child in. For example, in the constructor for Animal I might have something like this:
if(settings.animalType === "frog"){
this.animal = new Frog(settings);
}else [...] //other animal types
There are two main problems with this:
I have to call child functions like this: barry.animal.livesInTheForest(), which creates inconsistency as the parent functions can be called without the .animal.
The child classes (e.g. Frog) can no longer be child classes, as otherwise I will get too much recursion as it keeps trying to call itself with super().
I thought of a second method as well, which works like this:
In the parent (Animal) constructor:
//make sure this isn't being called from the child class
if(settings.animalType !== null){
if(settings.animalType === "frog"){
//set settings.animal null to avoid too much recursion
//this means that I can't set this.animalType though, so I can't access barry.animalType
settings.animalType = null;
//Is this something I can do?!
this = new Frog(settings);
} else [...] //other animal types
}
This works (I think), but I now can't set this.animalType to settings.animalType, meaning I can't write barry.animalType and get frog.
Also, this seems really hacky to me and I can't help thinking that there must be a better way to do this.
class Animal {
static create (settings) {
return new this.subClasses[settings.type](settings)
}
}
class Rabbit extends Animal {}
class Frog extends Animal {}
class Whale extends Animal {}
Animal.subClasses = { frog: Frog, rabbit: Rabbit, whale: Whale }
const animals = ['frog', 'rabbit', 'whale'].map((type) => Animal.create({ type }))
console.log({ animals })

Ecmascript 6 Class: access constructor created method of super class

I'm creating methods inside a constructor, to work with private values - this seems to be the suggested pattern to create private properties and limit the access to them.
Now I inherit from that class and within the constructor of the derived class I create another method, trying to access that. Unfortunately I get an exception TypeError: ... is not a function.
What to do? I can use the constructor as exactly that and just create a new object, but that would not be related to its classes and copying all the properties of the class and the super class etc. seems quite annoying and not like the right way. So again, what to do?
Here is an example:
class ClassA {
constructor() {
this.getValue = () => "private value";
}
}
class ClassB extends ClassA {
constructor() {
super();
this.getValue = () => `${super.getValue()}, second private value`;
}
}
const a = new ClassA();
const b = new ClassB();
console.log(`a.getValue = ${a.getValue()}`);
console.log(`b.getValue = ${b.getValue()}`);
Fiddle Check the console. The outcome I'd expect is "private value, second private value".
If someone can give me a hint, or a not super shitty workaround, I'd appreciate that.
Thanks, fea
super refers to the classes prototype. As your methods are not prototype methods but instance methods, it will unfortunately not work. However you could store the original function reference in a variable:
const orig = this.getValue.bind(this);
this.getValue = () => `${orig()}, second private value`;
However i personally rather like the _ pattern:
class ClassA {
constructor() {
this._private = "test";
}
getValue(){ return this._private; }
}
class ClassB extends ClassA {
constructor() {
super();
}
getValue(){
return `${super.getValue()}, second private value`;
}
}

Referencing static members from instance method on dynamically extended JS class

I have a base ES6 class that I dynamically extend given a configuration object, like so:
class Model {
constructor () {
// ...
}
save () {
// ...
}
}
function createModelFromConfig (config) {
const Impl = class extends Model {};
Object.assign(Impl, config);
return Impl;
}
const User = createModelFromConfig({store: new DbStore()});
In the save() method on the abstract Model, I'd like to reference the static object store, which will exist on the class that extends Model. This means, of course, that I need to reference a static member but the extended class is anonymous.
Just in a quick test using the Chrome console, I tried
function X () {}
X.prototype.doSomething = function () { console.log(this.constructor); };
function Y () {}
Y.prototype = Object.create(X.prototype);
new Y().doSomething(); // function X () {}
I don't know if this is a reliable test, but it appears that this.constructor does not reference the Impl that I extended, but instead the original base class, which isn't helpful.
A less elegant way is to add Impl.prototype.Impl = Impl; so I can use this.Impl.store in my save function, but it'd be preferable if I could access the static members of the Impl class without this.
Is my prototypal test in the console inadequate? Or is there any other way to access the constructor class in an instance method from an inherited method?
In my testing, I've concluded that Y.prototype = Object.create(X.prototype); is not an adequate equivalent to the ES6 extends implementation.
In running in the Node REPL:
class X {
constructor () {}
save () { console.log(this.constructor.z); }
}
class Y extends X {}
Y.z = 'z';
new Y().save(); // 'z'

ES6 class variable alternatives

Currently in ES5 many of us are using the following pattern in frameworks to create classes and class variables, which is comfy:
// ES 5
FrameWork.Class({
variable: 'string',
variable2: true,
init: function(){
},
addItem: function(){
}
});
In ES6 you can create classes natively, but there is no option to have class variables:
// ES6
class MyClass {
const MY_CONST = 'string'; // <-- this is not possible in ES6
constructor(){
this.MY_CONST;
}
}
Sadly, the above won't work, as classes only can contain methods.
I understand that I can this.myVar = true in constructor…but I don't want to 'junk' my constructor, especially when I have 20-30+ params for a bigger class.
I was thinking of many ways to handle this issue, but haven't yet found any good ones. (For example: create a ClassConfig handler, and pass a parameter object, which is declared separately from the class. Then the handler would attach to the class. I was thinking about WeakMaps also to integrate, somehow.)
What kind of ideas would you have to handle this situation?
2018 update:
There is now a stage 3 proposal - I am looking forward to make this answer obsolete in a few months.
In the meantime anyone using TypeScript or babel can use the syntax:
varName = value
Inside a class declaration/expression body and it will define a variable. Hopefully in a few months/weeks I'll be able to post an update.
Update: Chrome 74 now ships with this syntax working.
The notes in the ES wiki for the proposal in ES6 (maximally minimal classes) note:
There is (intentionally) no direct declarative way to define either prototype data properties (other than methods) class properties, or instance property
Class properties and prototype data properties need be created outside the declaration.
Properties specified in a class definition are assigned the same attributes as if they appeared in an object literal.
This means that what you're asking for was considered, and explicitly decided against.
but... why?
Good question. The good people of TC39 want class declarations to declare and define the capabilities of a class. Not its members. An ES6 class declaration defines its contract for its user.
Remember, a class definition defines prototype methods - defining variables on the prototype is generally not something you do.
You can, of course use:
constructor(){
this.foo = bar
}
In the constructor like you suggested. Also see the summary of the consensus.
ES7 and beyond
A new proposal for ES7 is being worked on that allows more concise instance variables through class declarations and expressions - https://esdiscuss.org/topic/es7-property-initializers
Just to add to Benjamin's answer — class variables are possible, but you wouldn't use prototype to set them.
For a true class variable you'd want to do something like the following:
class MyClass {}
MyClass.foo = 'bar';
From within a class method that variable can be accessed as this.constructor.foo (or MyClass.foo).
These class properties would not usually be accessible from to the class instance. i.e. MyClass.foo gives 'bar' but new MyClass().foo is undefined
If you want to also have access to your class variable from an instance, you'll have to additionally define a getter:
class MyClass {
get foo() {
return this.constructor.foo;
}
}
MyClass.foo = 'bar';
I've only tested this with Traceur, but I believe it will work the same in a standard implementation.
JavaScript doesn't really have classes. Even with ES6 we're looking at an object- or prototype-based language rather than a class-based language. In any function X () {}, X.prototype.constructor points back to X.
When the new operator is used on X, a new object is created inheriting X.prototype. Any undefined properties in that new object (including constructor) are looked up from there. We can think of this as generating object and class properties.
Babel supports class variables in ESNext, check this example:
class Foo {
bar = 2
static iha = 'string'
}
const foo = new Foo();
console.log(foo.bar, foo.iha, Foo.bar, Foo.iha);
// 2, undefined, undefined, 'string'
In your example:
class MyClass {
const MY_CONST = 'string';
constructor(){
this.MY_CONST;
}
}
Because of MY_CONST is primitive https://developer.mozilla.org/en-US/docs/Glossary/Primitive we can just do:
class MyClass {
static get MY_CONST() {
return 'string';
}
get MY_CONST() {
return this.constructor.MY_CONST;
}
constructor() {
alert(this.MY_CONST === this.constructor.MY_CONST);
}
}
alert(MyClass.MY_CONST);
new MyClass
// alert: string ; true
But if MY_CONST is reference type like static get MY_CONST() {return ['string'];} alert output is string, false. In such case delete operator can do the trick:
class MyClass {
static get MY_CONST() {
delete MyClass.MY_CONST;
return MyClass.MY_CONST = 'string';
}
get MY_CONST() {
return this.constructor.MY_CONST;
}
constructor() {
alert(this.MY_CONST === this.constructor.MY_CONST);
}
}
alert(MyClass.MY_CONST);
new MyClass
// alert: string ; true
And finally for class variable not const:
class MyClass {
static get MY_CONST() {
delete MyClass.MY_CONST;
return MyClass.MY_CONST = 'string';
}
static set U_YIN_YANG(value) {
delete MyClass.MY_CONST;
MyClass.MY_CONST = value;
}
get MY_CONST() {
return this.constructor.MY_CONST;
}
set MY_CONST(value) {
this.constructor.MY_CONST = value;
}
constructor() {
alert(this.MY_CONST === this.constructor.MY_CONST);
}
}
alert(MyClass.MY_CONST);
new MyClass
// alert: string, true
MyClass.MY_CONST = ['string, 42']
alert(MyClass.MY_CONST);
new MyClass
// alert: string, 42 ; true
Since your issue is mostly stylistic (not wanting to fill up the constructor with a bunch of declarations) it can be solved stylistically as well.
The way I view it, many class based languages have the constructor be a function named after the class name itself. Stylistically we could use that that to make an ES6 class that stylistically still makes sense but does not group the typical actions taking place in the constructor with all the property declarations we're doing. We simply use the actual JS constructor as the "declaration area", then make a class named function that we otherwise treat as the "other constructor stuff" area, calling it at the end of the true constructor.
"use strict";
class MyClass
{
// only declare your properties and then call this.ClassName(); from here
constructor(){
this.prop1 = 'blah 1';
this.prop2 = 'blah 2';
this.prop3 = 'blah 3';
this.MyClass();
}
// all sorts of other "constructor" stuff, no longer jumbled with declarations
MyClass() {
doWhatever();
}
}
Both will be called as the new instance is constructed.
Sorta like having 2 constructors where you separate out the declarations and the other constructor actions you want to take, and stylistically makes it not too hard to understand that's what is going on too.
I find it's a nice style to use when dealing with a lot of declarations and/or a lot of actions needing to happen on instantiation and wanting to keep the two ideas distinct from each other.
NOTE: I very purposefully do not use the typical idiomatic ideas of "initializing" (like an init() or initialize() method) because those are often used differently. There is a sort of presumed difference between the idea of constructing and initializing. Working with constructors people know that they're called automatically as part of instantiation. Seeing an init method many people are going to assume without a second glance that they need to be doing something along the form of var mc = MyClass(); mc.init();, because that's how you typically initialize. I'm not trying to add an initialization process for the user of the class, I'm trying to add to the construction process of the class itself.
While some people may do a double-take for a moment, that's actually the bit of the point: it communicates to them that the intent is part of construction, even if that makes them do a bit of a double take and go "that's not how ES6 constructors work" and take a second looking at the actual constructor to go "oh, they call it at the bottom, I see", that's far better than NOT communicating that intent (or incorrectly communicating it) and probably getting a lot of people using it wrong, trying to initialize it from the outside and junk. That's very much intentional to the pattern I suggest.
For those that don't want to follow that pattern, the exact opposite can work too. Farm the declarations out to another function at the beginning. Maybe name it "properties" or "publicProperties" or something. Then put the rest of the stuff in the normal constructor.
"use strict";
class MyClass
{
properties() {
this.prop1 = 'blah 1';
this.prop2 = 'blah 2';
this.prop3 = 'blah 3';
}
constructor() {
this.properties();
doWhatever();
}
}
Note that this second method may look cleaner but it also has an inherent problem where properties gets overridden as one class using this method extends another. You'd have to give more unique names to properties to avoid that. My first method does not have this problem because its fake half of the constructor is uniquely named after the class.
As Benjamin said in his answer, TC39 explicitly decided not to include this feature at least for ES2015. However, the consensus seems to be that they will add it in ES2016.
The syntax hasn't been decided yet, but there's a preliminary proposal for ES2016 that will allow you to declare static properties on a class.
Thanks to the magic of babel, you can use this today. Enable the class properties transform according to these instructions and you're good to go. Here's an example of the syntax:
class foo {
static myProp = 'bar'
someFunction() {
console.log(this.myProp)
}
}
This proposal is in a very early state, so be prepared to tweak your syntax as time goes on.
What about the oldschool way?
class MyClass {
constructor(count){
this.countVar = 1 + count;
}
}
MyClass.prototype.foo = "foo";
MyClass.prototype.countVar = 0;
// ...
var o1 = new MyClass(2); o2 = new MyClass(3);
o1.foo = "newFoo";
console.log( o1.foo,o2.foo);
console.log( o1.countVar,o2.countVar);
In constructor you mention only those vars which have to be computed.
I like prototype inheritance for this feature -- it can help to save a lot of memory(in case if there are a lot of never-assigned vars).
[Long thread, not sure if its already listed as an option...].
A simple alternative for contsants only, would be defining the const outside of class.
This will be accessible only from the module itself, unless accompanied with a getter.
This way prototype isn't littered and you get the const.
// will be accessible only from the module itself
const MY_CONST = 'string';
class MyClass {
// optional, if external access is desired
static get MY_CONST(){return MY_CONST;}
// access example
static someMethod(){
console.log(MY_CONST);
}
}
ES7 class member syntax:
ES7 has a solution for 'junking' your constructor function. Here is an example:
class Car {
wheels = 4;
weight = 100;
}
const car = new Car();
console.log(car.wheels, car.weight);
The above example would look the following in ES6:
class Car {
constructor() {
this.wheels = 4;
this.weight = 100;
}
}
const car = new Car();
console.log(car.wheels, car.weight);
Be aware when using this that this syntax might not be supported by all browsers and might have to be transpiled an earlier version of JS.
Bonus: an object factory:
function generateCar(wheels, weight) {
class Car {
constructor() {}
wheels = wheels;
weight = weight;
}
return new Car();
}
const car1 = generateCar(4, 50);
const car2 = generateCar(6, 100);
console.log(car1.wheels, car1.weight);
console.log(car2.wheels, car2.weight);
You can mimic es6 classes behaviour... and use your class variables :)
Look mum... no classes!
// Helper
const $constructor = Symbol();
const $extends = (parent, child) =>
Object.assign(Object.create(parent), child);
const $new = (object, ...args) => {
let instance = Object.create(object);
instance[$constructor].call(instance, ...args);
return instance;
}
const $super = (parent, context, ...args) => {
parent[$constructor].call(context, ...args)
}
// class
var Foo = {
classVariable: true,
// constructor
[$constructor](who){
this.me = who;
this.species = 'fufel';
},
// methods
identify(){
return 'I am ' + this.me;
}
}
// class extends Foo
var Bar = $extends(Foo, {
// constructor
[$constructor](who){
$super(Foo, this, who);
this.subtype = 'barashek';
},
// methods
speak(){
console.log('Hello, ' + this.identify());
},
bark(num){
console.log('Woof');
}
});
var a1 = $new(Foo, 'a1');
var b1 = $new(Bar, 'b1');
console.log(a1, b1);
console.log('b1.classVariable', b1.classVariable);
I put it on GitHub
Still you can't declare any classes like in another programming languages. But you can create as many class variables. But problem is scope of class object. So According to me, Best way OOP Programming in ES6 Javascript:-
class foo{
constructor(){
//decalre your all variables
this.MY_CONST = 3.14;
this.x = 5;
this.y = 7;
// or call another method to declare more variables outside from constructor.
// now create method level object reference and public level property
this.MySelf = this;
// you can also use var modifier rather than property but that is not working good
let self = this.MySelf;
//code .........
}
set MySelf(v){
this.mySelf = v;
}
get MySelf(v){
return this.mySelf;
}
myMethod(cd){
// now use as object reference it in any method of class
let self = this.MySelf;
// now use self as object reference in code
}
}
If its only the cluttering what gives the problem in the constructor why not implement a initialize method that intializes the variables. This is a normal thing to do when the constructor gets to full with unnecessary stuff. Even in typed program languages like C# its normal convention to add an Initialize method to handle that.
Just define a getter.
class MyClass
{
get MY_CONST () { return 'string'; }
constructor ()
{
console.log ("MyClass MY_CONST:", this.MY_CONST);
}
}
var obj = new MyClass();
The way I solved this, which is another option (if you have jQuery available), was to Define the fields in an old-school object and then extend the class with that object. I also didn't want to pepper the constructor with assignments, this appeared to be a neat solution.
function MyClassFields(){
this.createdAt = new Date();
}
MyClassFields.prototype = {
id : '',
type : '',
title : '',
createdAt : null,
};
class MyClass {
constructor() {
$.extend(this,new MyClassFields());
}
};
-- Update Following Bergi's comment.
No JQuery Version:
class SavedSearch {
constructor() {
Object.assign(this,{
id : '',
type : '',
title : '',
createdAt: new Date(),
});
}
}
You still do end up with 'fat' constructor, but at least its all in one class and assigned in one hit.
EDIT #2:
I've now gone full circle and am now assigning values in the constructor, e.g.
class SavedSearch {
constructor() {
this.id = '';
this.type = '';
this.title = '';
this.createdAt = new Date();
}
}
Why? Simple really, using the above plus some JSdoc comments, PHPStorm was able to perform code completion on the properties. Assigning all the vars in one hit was nice, but the inability to code complete the properties, imo, isn't worth the (almost certainly minuscule) performance benefit.
Well, you can declare variables inside the Constructor.
class Foo {
constructor() {
var name = "foo"
this.method = function() {
return name
}
}
}
var foo = new Foo()
foo.method()
Recent browsers as of 2021 (not IE, see MDN browser chart) implement Public class fields which seems to be what you're looking for:
class MyClass {
static foo = 3;
}
console.log(MyClass.foo);
However apparently it's not possible to make this a const: Declaring static constants in ES6 classes?
A static getter looks pretty close:
class MyClass {
static get CONST() {
return 3;
}
}
MyClass.CONST = 4; // property unaffected
console.log(MyClass.CONST);
This is a bit hackish combo of static and get works for me
class ConstantThingy{
static get NO_REENTER__INIT() {
if(ConstantThingy._NO_REENTER__INIT== null){
ConstantThingy._NO_REENTER__INIT = new ConstantThingy(false,true);
}
return ConstantThingy._NO_REENTER__INIT;
}
}
elsewhere used
var conf = ConstantThingy.NO_REENTER__INIT;
if(conf.init)...

Categories

Resources