javascript - Check if parent methods are used inside child methods - javascript

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.

Related

Efficient and elegant way to create nested ES6 classes?

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();

javascript good way to create or design Singleton class

I think in javascript it could create Singleton class like below
test.js
class Container {
constructor() {
this.map = new Map;
}
set(key, value) {
this.map.set(key, value);
}
get(key) {
return this.map.get(key);
}
}
module.exports.Container = new Container();
so that
I could use that other files like in index.js
import container from './test'
However the constructor of Container need some other parameters so that it could do better. And the Container also need to be Singleton class ,
because it is a common class . And i do not know how to create it?
constructor(servie) {
this.map = new Map;
this.servie = servie;
}
by the way the service variable is created when application starts
EDIT: I've realised my naming is slightly confusing, as I've used Container to represent the IoC container, and then realised you've used that name to represent your Singleton.
Providing you are able to instantiate your singleton at a central point of code (entry point file for example, while setting up server). You can achieve a Singleton in a number of ways. For example:
let instance = null;
class Singleton {
constructor(arg1, arg2) {
this.arg1 = arg1;
this.arg2 = arg2;
}
}
module.exports = (...args) => {
return instance || (instance = new Singleton(...args));
}
You could do this any multitude of ways e.g. with static functions instead of the anonymous function. However, with all of these I think it becomes slightly confused as to whether you're init'ing the instance or just trying to retrieve it.
IoC container patterns (often used in C# projects) are a little cleaner in my opinion. Where you might have a single container class that keeps track of your singletons and provides separate access and initiation functions.
class Container {
constructor() {
this.instances = {};
}
add(name, instance) {
this.instances[name] = instance; // depends if you want overwrite or not
}
get(name) {
if (!this.instances[name]) {
throw new Error('some appropriate error messaging for your app');
}
return this.instances[name];
}
remove(name) {
delete this.instances[name];
}
}
module.exports = new Container(); // this has to be a no-param constructor
Then in your app init code, you initialise the singletons and register them with your container:
const Container = require('<path_to_Container>');
const Singleton = require('<path_to_Singleton>');
...
Container.add('mySingleton', new Singleton(arg1, arg2));
and access it at any time with:
Container.get('mySingleton');
Couple notes:
You could use Singleton.name instead of a string name ('mySingleton' in the example)
This is just a personal preference. But I like it because it feels more declarative, you register (add) an instance to the container at app start-up and don't blur the lines between initialisation and usage. And it means that if you add more of these singletons going forward, you have a single pattern for it already in place.

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.

Split a Javascript class (ES6) over multiple files?

I have a Javascript class (in ES6) that is getting quite long. To organize it better I'd like to split it over 2 or 3 different files. How can I do that?
Currently it looks like this in a single file:
class foo extends bar {
constructor(a, b) {} // Put in file 1
methodA(a, b) {} // Put in file 1
methodB(a, b) {} // Put in file 2
methodC(a, b) {} // Put in file 2
}
Thanks!
When you create a class
class Foo extends Bar {
constructor(a, b) {
}
}
you can later add methods to this class by assigning to its prototype:
// methodA(a, b) in class Foo
Foo.prototype.methodA = function(a, b) {
// do whatever...
}
You can also add static methods similarly by assigning directly to the class:
// static staticMethod(a, b) in class Foo
Foo.staticMethod = function(a, b) {
// do whatever...
}
You can put these functions in different files, as long as they run after the class has been declared.
However, the constructor must always be part of the class declaration (you cannot move that to another file). Also, you need to make sure that the files where the class methods are defined are run before they are used.
Here's my solution. It:
uses regular modern classes and .bind()ing, no prototype. (EDIT: Actually, see the comments for more on this, it may not be desirable.)
works with modules. (I'll show an alternative option if you don't use modules.)
supports easy conversion from existing code.
yields no concern for function order (if you do it right).
yields easy to read code.
is low maintenance.
unfortunately does not play well with static functions in the same class, you'll need to split those off.
First, place this in a globals file or as the first <script> tag etc.:
BindToClass(functionsObject, thisClass) {
for (let [ functionKey, functionValue ] of Object.entries(functionsObject)) {
thisClass[functionKey] = functionValue.bind(thisClass);
}
}
This loops through an object and assigns and binds each function, in that object, by its name, to the class. It .bind()'s it for the this context, so it's like it was in the class to begin with.
Then extract your function(s) from your class into a separate file like:
//Use this if you're using NodeJS/Webpack. If you're using regular modules,
//use `export` or `export default` instead of `module.exports`.
//If you're not using modules at all, you'll need to map this to some global
//variable or singleton class/object.
module.exports = {
myFunction: function() {
//...
},
myOtherFunction: function() {
//...
}
};
Finally, require the separate file and call BindToClass like this in the constructor() {} function of the class, before any other code that might rely upon these split off functions:
//If not using modules, use your global variable or singleton class/object instead.
let splitFunctions = require('./SplitFunctions');
class MySplitClass {
constructor() {
BindToClass(splitFunctions, this);
}
}
Then the rest of your code remains the same as it would if those functions were in the class to begin with:
let msc = new MySplitClass();
msc.myFunction();
msc.myOtherFunction();
Likewise, since nothing happens until the functions are actually called, as long as BindToClass() is called first, there's no need to worry about function order. Each function, inside and outside of the class file, can still access any property or function within the class, as usual.
I choose to have all privte variables/functions in an object called private, and pass it as the first argument to the external functions.
this way they have access to the local variables/functions.
note that they have implicit access to 'this' as well
file: person.js
const { PersonGetAge, PersonSetAge } = require('./person_age_functions.js');
exports.Person = function () {
// use privates to store all private variables and functions
let privates={ }
// delegate getAge to PersonGetAge in an external file
// pass this,privates,args
this.getAge=function(...args) {
return PersonGetAge.apply(this,[privates].concat(args));
}
// delegate setAge to PersonSetAge in an external file
// pass this,privates,args
this.setAge=function(...args) {
return PersonSetAge.apply(this,[privates].concat(args));
}
}
file: person_age_functions.js
exports.PersonGetAge =function(privates)
{
// note: can use 'this' if requires
return privates.age;
}
exports.PersonSetAge =function(privates,age)
{
// note: can use 'this' if requires
privates.age=age;
}
file: main.js
const { Person } = require('./person.js');
let me = new Person();
me.setAge(17);
console.log(`I'm ${me.getAge()} years old`);
output:
I'm 17 years old
note that in order not to duplicate code on person.js, one can assign all functions in a loop.
e.g.
person.js option 2
const { PersonGetAge, PersonSetAge } = require('./person_age_functions.js');
exports.Person = function () {
// use privates to store all private variables and functions
let privates={ }
{
// assign all external functions
let funcMappings={
getAge:PersonGetAge,
setAge:PersonSetAge
};
for (const local of Object.keys(funcMappings))
{
this[local]=function(...args) {
return funcMappings[local].apply(this,[privates].concat(args));
}
}
}
}
You can add mixins to YourClass like this:
class YourClass {
ownProp = 'prop'
}
class Extension {
extendedMethod() {
return `extended ${this.ownProp}`
}
}
addMixins(YourClass, Extension /*, Extension2, Extension3 */)
console.log('Extended method:', (new YourClass()).extendedMethod())
function addMixins() {
var cls, mixin, arg
cls = arguments[0].prototype
for(arg = 1; arg < arguments.length; ++ arg) {
mixin = arguments[arg].prototype
Object.getOwnPropertyNames(mixin).forEach(prop => {
if (prop == 'constructor') return
if (Object.getOwnPropertyNames(cls).includes(prop))
throw(`Class ${cls.constructor.name} already has field ${prop}, can't mixin ${mixin.constructor.name}`)
cls[prop] = mixin[prop]
})
}
}
TypeScript Solution
foo-methods.ts
import { MyClass } from './class.js'
export function foo(this: MyClass) {
return 'foo'
}
bar-methods.ts
import { MyClass } from './class.js'
export function bar(this: MyClass) {
return 'bar'
}
class.ts
import * as barMethods from './bar-methods.js'
import * as fooMethods from './foo-methods.js'
const myClassMethods = { ...barMethods, ...fooMethods }
class _MyClass {
baz: string
constructor(baz: string) {
this.baz = baz
Object.assign(this, myClassMethods);
}
}
export type MyClass = InstanceType<typeof _MyClass> &
typeof myClassMethods;
export const MyClass = _MyClass as unknown as {
new (
...args: ConstructorParameters<typeof _MyClass>
): MyClass;
};
My solution is similar to the one by Erez (declare methods in files and then assign methods to this in the constructor), but
it uses class syntax instead of declaring constructor as a function
no option for truly private fields - but this was not a concern for this question anyway
it does not have the layer with the .apply() call - functions are inserted into the instance directly
one method per file: this is what works for me, but the solution can be modified
results in more concise class declaration
1. Assign methods in constructor
C.js
class C {
constructor() {
this.x = 1;
this.addToX = require('./addToX');
this.incX = require('./incX');
}
}
addToX.js
function addToX(val) {
this.x += val;
return this.x;
}
module.exports = addToX;
incX.js
function incX() {
return this.addToX(1);
}
module.exports = incX;
2. Same, but with instance fields syntax
Note that this syntax is a Stage 3 proposal as of now.
But it works in Node.js 14 - the platform I care about.
C.js
class C {
x = 1;
addToX = require('./addToX');
incX = require('./incX');
}
Test
const c = new C();
console.log('c.incX()', c.incX());
console.log('c.incX()', c.incX());

Inheritance of static members in MooTools

I'm using the MooTools class system, and I'd like to be able to access any static member of a particular class without having to know the full inheritance chain. For example, if I have a ChildClass that extends BaseClass, and
BaseClass.foo = function() { /*...*/ }
I'd like to be able to call ChildClass.foo().
To this end, I'm thinking of modifying the MooTools Class method as follows:
function Class(params)
// ...
// var newClass = ...
var parentClass = params.Extends;
if (parentClass) {
newClass.__proto__ = parentClass;
}
// ...
}
This will set up each class object's prototype chain to point to its parent class.
If a static member from a higher class is hidden in a more derived class, so be it.
Notwithstanding the use of the deprecated __proto__, am I on the right track here? Does anyone see any glaring problems?
You can always extend the Extend Mutator:
(function(){
var original = Class.Mutators.Extends;
Class.Mutators.Extends = function(parent) {
original.call(this, parent);
var members = {};
for (var key in parent) {
if (parent.hasOwnProperty(key) && !this.hasOwnProperty(key)) {
members[key] = parent[key];
}
}
this.extend(members);
};
}());
You should check out Mark Obcena's book.

Categories

Resources