Efficient and elegant way to create nested ES6 classes? - javascript

While trying to find a way to use nested classes in JS, I came up with this sort of thing:
class Character {
constructor() {
this.Info= class I {
constructor(name,value) {
this.name=name;
this.value=value;
}
};
}
bar () {
var trial = new this.Info("Goofy", 2);
alert(trial.name);
}
}
var test = new Character();
test.bar();
and it seems to work. However, I'm afraid this might be creating a new function object for each new call, as I define the class in the constructor (which is executed at each new call). Is there a more efficient way of doing this?
This question does not solve my issue as the author only wonders how to even have a nested class; I'm already able to do that but I wonder if there's a more efficient way.

Using a static property in react, angular or just using babel, because direct static class properties are not currently implemented on all browsers.
class Character {
static Info = class I {
constructor(name) { this.name=name; }
}
bar () {
return new Character.Info("Goofy");
}
}
const test = new Character();
console.log(test.bar());
Using a static property the old way -- currently working on all browsers.
class Character {
bar () { return new Character.Info("Goofy"); }
}
Character.Info = class I {
constructor(name) { this.name=name; }
}
const test = new Character();
console.log(test.bar());

Maybe the example you've given is too simple to demonstrate whatever problem you're trying to solve, but it seems to me you don't need to nest them at all.
class Info {
constructor(name, value) {
this.name = name;
this.value = value;
}
}
class Character {
bar() {
var trial = new Info("Goofy", 2);
console.log(trial.name);
}
}
const test = new Character();
test.bar();

Related

Javascript ES6 generic builder pattern for large hierarchy

I am trying to come up with a generic builder function for a large hierarchy of objects in Javascript. The goal is to implement as much functionality as possible at the top of the hierarchy. After playing around for a little while, I have ended up with a structure I like, although am not completely happy with.
The structure currently looks somewhat like this. I have attached a working (simplified) version below:
class AbstractItem {
constructor(build) {
if (this.constructor === AbstractItem) {
throw new TypeError("Oops! AbstractItem should not be instantiated!");
}
this._id = build.id;
}
/*
The generic ItemBuilder is part of an abstract superclass.
Every item should have an ID, thus the builder validates this.
It also provides a generic build() function, so it does not have to be re-implemented by every subclass.
*/
static get Builder() {
/*
This constant references the constructor of the class for which the Builder function was called.
So, if it was called for ConcreteItem, it will reference the constructor of ConcreteItem.
This allows us to define a generic build() function.
*/
const BuildTarget = this;
class ItemBuilder {
constructor(id) {
if (!id) {
throw new TypeError('An item should always have an id!');
}
this._id = id;
}
//The generic build method calls the constructor function stored in the BuildTarget variable and passes the builder to it.
build() {
return new BuildTarget(this);
}
get id() {
return this._id;
}
}
return ItemBuilder;
}
doSomething() {
throw new TypeError("Oops! doSomething() has not been implemented!");
}
get id() {
return this._id;
}
}
class AbstractSubItem extends AbstractItem {
constructor(build) {
super(build);
if (this.constructor === AbstractSubItem) {
throw new TypeError("Oops! AbstractSubItem should not be instantiated!");
}
this._name = build.name;
}
/*
AbstractSubItem implements a different version of the Builder that also requires a name parameter.
*/
static get Builder() {
/*
This builder inherits from the builder used by AbstractItem by calling the Builder getter function and thus retrieving the constructor.
*/
class SubItemBuilder extends super.Builder {
constructor(id, name) {
super(id);
if (!name) {
throw new TypeError('A subitem should always have a name!');
}
this._name = name;
}
get name() {
return this._name;
}
}
return SubItemBuilder;
}
get name() {
return this._name;
}
}
class ConcreteItem extends AbstractItem {
doSomething() {
console.log('Hello world! My name is ' + this.id + '.');
}
}
class ConcreteSubItem extends AbstractSubItem {
doSomething() {
console.log('Hello world! My name is ' + this.name + ' (id: ' + this.id + ').');
}
}
new ConcreteItem.Builder(1).build().doSomething();
new ConcreteSubItem.Builder(1, 'John').build().doSomething();
In my opinion, there are some pros and cons to my current approach.
Pros
The Builder() method provides a common interface that can be used to obtain a builder for all implementing classes.
My concrete classes can inherit the builder class without any additional effort.
Using inheritance, the builder can be easily expanded if needed.
The builder code is part of the abstract class, so it is clear what is being built when reading the code.
The calling code is easy to read.
Cons
It is not clear, looking at the Builder() getter function, which parameters are required to avoid an exception. The only way to know this is to look at the constructor (or at the comments), which is buried a couple of layers deep.
It feels counter-intuitive having the SubItemBuilder inherit from super.Builder, rather than a top-level class. Likewise, it may not be clear to other how to inherit from the ItemBuilder without looking at the SubItemBuilder example.
It is not really clear, looking at the AbstractItem class, that it should be constructed using the builder.
Is there a way to improve my code to negate some of the cons I've mentioned? Any feedback would be very much appreciated.

how to extends es6 class using Dojo

I have some es6 class
class Human {
constructor(){
this.age = 0;
}
}
I want to inherit from this class using dojo toolkit
define(["dojo/_base/declare"],
function (declare) {
return declare("Man", Human, {
});
});
I'm getting the error Class constructor Human cannot be invoked without 'new'.
Tried to inherit from Human.constructor and from Human.funcWrapper
class Human {
constructor(){
this.age = 0;
}
static funcWrapper(){
return new Human()
}
}
Nothing worked.
I know that I can use babel to transform my code to functions but I don't want because of some political reasons.
As usual, i'm posting banging my head for hours, and then i'm coming with a solution. So far i've tested it, and look like it is working good. including calling this.inherited(arguments, ["bla"]) (dojo way of calling super("bla"))
So, I've created this function to convert es6 class to function class
function funcClass(type) {
const FuncClass = function (...args) {
const _source = Reflect.construct(type, args, this.constructor);
const keys = Reflect.ownKeys(_source);
for (const key of keys) {
if (!key.match || !key.match(/^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/)) {
const desc = Object.getOwnPropertyDescriptor(_source, key);
!this[key] && Object.defineProperty(this, key, desc);
}
}
}
FuncClass.prototype = type.prototype;
return FuncClass;
}
And usage:
define(["dojo/_base/declare"],
function (declare) {
return declare("Man", funcClass(Human), {
});
});

es6 classes and adding an explicit prototype to share across class instances

So, I have a class:
class XYZ {
constructor(name) {
if (!new.target) new XYZ(name);
this.name = name;
}
run() {
}
walk() {
}
addShoe() {
// I want to add a shoe here IF it hasn't already been
// added, by any other instance
}
}
Now, I need all of my instances to share a variable, ie: SHOES.
The only way I am figuring to do this, well two ways is either add a variable outside the class ( top of module ) OR put it on the prototype.
ie
I've tried adding static variables, not working. Of course I could just use a set/get, but again - that is a method that would still have to "access/update" the "shared variable resource"..
let SHOES = new Set();
//or
XYZ.prototype.SHOES = new Set();
class XYZ {
constructor(name) {
if (!new.target) new XYZ(name);
this.name = name;
}
run() {
}
walk() {
}
}
is there a more intuitive way to do this? Of course, this is a very simplified version - but trying to go with es6 'way of doing things'.
I ended going with the following. but it still seems wrong. Your thoughts?
XYZ.SHOES = new Set();
I believe I also tried the following and not working:
class XYZ {
constructor() {
static SHOES = new Set(); // <-- didn't work
this.constructor.SHOES = new Set(); // <-- didn't work
}
}
XYZ.SHOES = new Set(); // <- worked
let SHOES = new Set(); // <-- worked
Of course, declaring a global in the module worked AND just assigning to the class worked, but these seem dirty. Any thoughts on a more succinct approach?

javascript - Check if parent methods are used inside child methods

I'm writing some JS that extends a parent class and I wanted to know if there's a way to tell if a child class is using a parent method without having called it yet. Ideally I'd like to run a check in the constructor of the parent to see if any of the child methods are using the parent's methods in the method definition.
I've done a bit of research and have come across things like Object.getOwnPropertyNames() but I'm not sure if I'm headed in the right direction.
For instance:
class Path {
constructor (name) {
// how can I check if addRelationship have been used? If possible.
this.relationships = {};
this.currentRelationship = '';
this.path = path;
}
addRelationship (relationship) {
// do something
this.currentRelationship = relationship.path;
return this;
}
makePath () {
let path = [this.path];
if(this.currentRelationship) {
path.push(this.currentRelationship)
}
return path.join("/");
}
}
class OnePath extends Path {
// ...
someMethodFromThatRelationship () { }
}
class TwoPath extends Path {
// ...
}
var onePath = new OnePath('one');
var twoPath = new TwoPath('two-path');
class SomeOtherPath extends Path {
one () {
return this.addRelationship(onePath);
}
two () {
return this.addRelationship(twoPath);
}
}
The idea of the above example is I could check if addRelationship is referenced in any methods and if so, register a this.relationships.one and this.relationships.two before one() and two() are actually called. I hope I'm making sense. I'd love to know if this is even possible.
Updated
The end result of the above code would be the ability to do the following:
let someOtherPath = new SomeOtherPath('some-other-path');
// now I can call
someOtherPath.relationships.one.someMethodFromThatRelationship();
// and can also call the save method from the extended class
someOtherPath.one().makePath();
// some-other-path/one
// I can also just call
someOtherPath.makePath();
// some-other-path
Is there a way to tell if a child class is using a parent method without having called it yet?
No. Figuring out what programs do without calling them is equivalent to the unsolvable halting problem.
I think what you are actually looking for is a more declarative approach for creating the relationship and its accompanying method in one go. Don't use too much magic (which a parent constructor inspecting its child class code would certainly be) but be explicit.
class Path {
constructor (path) {
this.relationships = {};
this.currentRelationship = '';
this.path = path;
}
addRelationship (name, relationship) {
this.relationships[name] = relationship;
this[name] = function() {
// do something
this.currentRelationship = name;
return this.relationships[name];
}
return this;
}
makePath () {
let path = this.path;
if (this.currentRelationship) {
path += "/" + this.relationships[this.currentRelationship].makePath();
}
return path;
}
}
class SomeOtherPath extends Path {
constructor(name) {
super(name);
this.addRelationship("one", new OnePath('one'));
this.addRelationship("two", new TwoPath('two-path'));
}
}
or even
class Path {
constructor (path, relationships = {}) {
this.relationships = relationships;
this.currentRelationship = '';
this.path = path;
for (let const r in relationships)
this.addRelationship(r, relationships[r]);
}
…
}
class SomeOtherPath extends Path {
constructor(name) {
super(name, {
one: new OnePath('one'),
two: new TwoPath('two-path')
});
}
}
Maybe you don't even need these child classes any more if they don't have other methods or are only instantiated once (as singletons).
Notice that the above approach will create new methods and new subpaths on every instantiation of the constructor, if you don't want that you can of course also put the declaration on the class statically. Just make addRelationShip a static method that initialises the default relationships objects and puts the methods on the class' .prototype. The variations of the pattern are endless.
You even might want to experiment with the proposed decorators feature for classes.

How to access a method from a class from another class?

I want to use Object Oriented Programming technique with JavaScript but I can't access a method from one class from another class. How can do like the following?
class one{
write(){
console.log("Yes! I did!");
}
}
class two{
var object=new one();
tryingMethod(){
object.write();
}
}
I get the following error:
Uncaught SyntaxError: Unexpected identifier -->> for var object=new one();
Your syntax is not legal. There should be an error in your console showing you which line of code is not correct.
If it's a static method (doesn't use any instance data), then declare it as a static method and you can directly call it.
If it's an instance method, then you would typically create an object of type one and then call the method on that object (usually in the constructor).
To make the method static (which appears to be fine in your specific case):
class One {
static write(){
console.log("Yes! I did!");
}
}
class Two {
tryingMethod(){
One.write();
}
}
For the non-static case, you don't have the proper syntax. It appears you want to create the instance of the One object in a constructor for Two like this:
class One {
write(){
console.log("Yes! I did!");
}
}
class Two {
constructor() {
this.one = new One();
}
tryingMethod(){
this.one.write();
}
}
var x = new Two();
x.tryingMethod();
Note: I'm also following a common Javascript convention of using an identifier that starts with a capital letter for the class/constructor name such as One instead of one.
What I'd recommend doing is not tying the classes together so tightly and doing something like this...
class One {
write() {
console.log("Yes! I did!");
}
}
class Two {
constructor(one = new One()) {
this.one = one;
}
tryingMethod() {
this.one.write();
}
}
Now what you can do is...
const two = new Two();
two.write();
This allows you to have a better separation of concerns and allows you to more easily unit test the Two class because you can pass in a mock implementation of the One class if you want.
describe("two class", () => {
it("should do something", () => {
const one = {
write: sinon.spy()
};
const two = new Two(one)
two.tryingMethod();
expect(one.write.calledOnce).to.be.ok;
});
});
You can pack dependencies in a container.
class Provider {
private _one?: One;
private _two?: Two;
one(): One {
return this._one || (this._one = new One(this));
}
two(): Two {
return this._two || (this._two = new Two(this));
}
}
class One {
constructor(private provider: Provider) { }
write() {
console.log("Yes! I did!");
}
}
class Two {
constructor(private provider: Provider) { }
tryingMethod() {
this.provider.one().write();
}
}

Categories

Resources