ES6 / Meteor Singleton pattern with inheritance - javascript

I have programmed a singleton using this blog:
http://amanvirk.me/singleton-classes-in-es6/
But I need a singleton on both client and server side.
That is, I have three programs: common.js
export class AppsManagerCommon {
constructor(options) {
// general options
this.options = options;
// list of registered apps
this.apps = [];
}
registerApp(app) {
a = this.apps.find(function (a) {return a.name === app.name;});
console.log(a);
this.apps.push(app);
}
}
client.js
import { AppsManagerCommon } from 'common.js';
let instanceAppsManager = null; // singleton pattern
export class AppsManager extends AppsManagerCommon {
constructor(options) {
if (!instanceAppsManager) { // not yet instantiated
super(options);
instanceAppsManager = this;
}
return instanceAppsManager;
}
}
server.js (identical to client.js)
import { AppsManagerCommon } from 'common.js';
let instanceAppsManager = null; // singleton pattern
export class AppsManager extends AppsManagerCommon {
constructor(options) {
if (!instanceAppsManager) { // not yet instantiated
super(options);
instanceAppsManager = this;
}
return instanceAppsManager;
}
}
The singletoc can be used sucessfully with:
a = new AppsManager();
a.registerApp({name:'app1'});
but as soon as I do
b = new AppsManager(); // should be the same instance
I get an error:
ReferenceError: this hasn't been initialised - super() hasn't been called
at BabelRuntime.possibleConstructorReturn (packages/babel-runtime.js:206:13)
I can more or less understand what the error means, but I have no clue how I could resolve the issue.
EDIT 1
NB The existence check in registerApp does not work, but is not a problem for now

This is a working solution:
common.js
let instanceAppsManager = null; // singleton pattern
export class AppsManagerCommon {
constructor(options) {
if (!instanceAppsManager) { // not yet instantiated
instanceAppsManager = this;
// general options
this.options = options;
// list of registered apps
this.apps = [];
}
return instanceAppsManager;
}
registerApp(app) {
this.apps.push(app);
}
}
server.js (identical to client.js)
import { AppCommon, AppsManagerCommon } from 'common.js';
export class AppsManager extends AppsManagerCommon {
constructor(options) {
super(options);
}
}

Related

A way to retrieve the context without passing `.this` as parameter

Here is a little background.
So I have my parent class. Constructor receives two objects: the first one is the class where the instance of the parent object was created (using others classes which are inheriting parent class).
The second object are just some parameters
parent.js
class ParentClass {
constructor(nativeObject, params) {
this.nativeObject = nativeObject;
this.param1 = params.param1;
this.param2 = params.param2;
}
// some logic here
}
module.exports = { ParentClass };
child.js
class ChildClass extends ParentClass {
// some logic here
}
module.exports = { ChildClass };
I'm using the child class in the other class methods for creating it. I can have multiple files like below:
usingclasses.js
let { ChildClass } = require('path/to/my/child/class');
let opts = {
param1: 'idkSomeString';
param2: 42;
}
class MyCoolClass {
createChild() {
return new ChildClass(this, opts);
}
}
module.exports = { MyCoolClass };
anotherclasses.js
let { ChildClass } = require('path/to/my/child/class');
let opts = {
param1: 'thatAstringToo';
param2: 123;
}
class AnotherCoolClass{
alsoCreatesChild() {
return new ChildClass(this, opts);
}
}
module.exports = { AnotherCoolClass};
I can create multiple instances of the ChildClass in a different methods within one class:
thirdexample.js
let { ChildClass } = require('path/to/my/child/class');
let opts1 = {
param1: 'aStringAgain';
param2: 27;
}
let opts2 = {
param1: 'ABC';
param2: 33;
}
class ClassAgain{
firstMethod() {
return new ChildClass(this, opts1);
}
secondMethod() {
return new ChildClass(this, opts2);
}
}
module.exports = { ClassAgain };
The reason why I'm passing .this because I'm working with it in ParentClass
e.g for logging console.log(`I know this was created in ${this.nativeObject .constructor.name}`);
What I'm looking for is the way to get rid of passing .this every time I'm creating a new instance of the ChildClass
Reason behind this is that in one file (class) I can create up to 10+ instances of the ChildClass.
In the previous question I already got an answer which helped me to make the code more structured and more easy to maintain:
Instead of:
createChild() {
return new ChildClass(this, param1, param2, param3, paramN);
}
I'm doing:
createChild() {
return new ChildClass(this, paramObj);
}
So now I can store all my objects with parameters at the top of the file (of even separate file) and easily find and change it if needed.
...
Now I'm trying to find a way to get rid of .this every time I'm creating ChildClass
Is there any other way how ParentClass can know where does this ChildClass instances "lives"?
I'm trying to figure out can I do something using .apply or .call but seems like this is not a droids I'm looking for
UPDATE:
Since I have zero progress on this and I even got a comment that most likely this is not possible let's assume for now that I need a name of the class that is creating instance of the ChildClass only for logging.
How can I retrieve the name of the class without passing it every time when creating a ChildClass?
let opts = {
className: 'MyCoolClass'
param1: 'idkSomeString';
param2: 42;
}
class MyCoolClass {
createChild() {
return new ChildClass(opts);
}
}
module.exports = { MyCoolClass };
The problem is that I will have to set className: 'MyCoolClass' in each version of opts again and again and again... Just repeat the same for 10-15 times
You can just instantiate your classes using Reflect.construct.
class ParentClass {
constructor (nat, x) {
this.nat = nat
this.x = x
}
log () {
console.log('from ', this.nat.constructor.name, this.x)
}
}
class ChildClass extends ParentClass{}
class ClassAgain{
firstMethod () {
return this.makeInstance({ a: 1 })
}
secondMethod () {
return this.makeInstance({ b: 2 })
}
makeInstance (params) {
return Reflect.construct(ChildClass, [this, params])
}
}
const ca = new ClassAgain()
ca.firstMethod().log()
ca.secondMethod().log()

Extending array fails in JavaScript ES6

I've some problems to extend an JS array. These are my classes:
// ArrayList.js
export default class ArrayList extends Array {
clear() {
this.splice(0, this.length);
}
}
// MyItem.js
export default class MyItem {
constructor() {
this._id = 0;
}
getID() {
return this._id;
}
setID(value) {
if (typeof value === 'number') {
this._id = value;
}
}
}
// Test.js
import ArrayList from './ArrayList';
import MyItem from './MyItem';
let list = new ArrayList();
let item1 = new MyItem();
item1.setID(1);
list.push(item1);
let item2 = new MyItem();
item2.setID(2);
list.push(item2);
If I now execute:
list.forEach(function(item) {
console.log(item.getID());
});
Everything works perfect but if I try to call my custom method I run into errors:
list.clear();
console.log(list.length);
The exception is:
TypeError: list.clear is not a function
+++ UPDATE +++
I use the test script with node.js:
node start.js
That's my start.js:
require('babel-register')({
presets: [ 'env' ]
})
module.exports = require('./Test.js')
And then every class is stored in a separate JS file.
I don't like your import end exports. Try modules (https://nodejs.org/api/modules.html) and this should work without Babel in Node.
module.exports = class MyItem {
// class content
}
module.exports = class ArrayList extends Array {
clear() {
this.splice(0, this.length);
}
}
// in the test file
const ArrayList = require('./ArrayList');
const MyItem = require('./MyItem');

How to deal with fn.call(this) replacing original this

To begin, I have an application that is used for managing employees. When the user creates a new instance of the application, I would like for them to have an option to submit a function that will run before anything else in the application does. The problem is that I need to add functionality to the end of that function so I need it passed back to the application.
However, if I use fn.call(this) in the StateManager.js class, it then overrides the this of state manager and gets rid of the functionality of StateManager. The exact error returned is Uncaught TypeError: this.onPreload is not a function
Essentially, when a new instance is created, I would like to take the user's preload function and pass it to StateManager.js where it will be adjusted.
Here is demonstration code:
class Application {
constructor(options = {}) {
return new User(options);
}
}
class User {
constructor(options) {
this._options = options;
this.state = new StateManager(this);
this.job = new Job(this);
this.init();
}
init() {
this.state.onPreload = this._options.preload;
this.state.preload.call(this);
}
}
class Job {
constructor(user) {
this.user = user;
}
changeTitle(title) {
this.user.jobTitle = title;
}
}
class StateManager {
constructor(user) {
this.user = user;
this.onPreload = null;
}
preload() {
this.onPreload();
}
}
const options = {
preload: preload
};
const app = new Application(options);
function preload() {
app.job.changeTitle('CEO');
}
index.js
import { Application } from './Application.js';
const options = {
preload: preload
};
const app = new Application(options);
function preload() {
// Access some irrelevant function in job that sets a new value
app.job.changeTitle('CEO');
}
app.js
import { User } from './User.js';
export class Application {
constructor(options = {}) {
return new User(options);
}
}
user.js
import { StateManager } from './StateManager.js';
import { Job } from './Job.js';
export class User {
constructor(options = {}) {
this._options = options;
this.state = new StateManager(this);
this.job = new Job(this);
this.init();
}
init() {
this.state.onPreload = this._options.preload;
this.state.preload.call(this);
}
}
statemanager.js
export class StateManager {
constructor(user) {
this.user = user;
this.onPreload = null;
}
preload() {
this.onPreload();
// My custom functionality to add at the end.
}
}
preload() is referring to the global variable app, but it's being called in the function used to initialize app in the first place. It needs to receive the User object being initialized, rather than referring to the global variable.
Use this.state.onPreload = this._options.preload.bind(this); to bind the context of the preload function to that object.
You could also change StateManager.preload() to use this.onPreload.call(this.user);. But this might create an inappropriate dependency that doesn't apply in all cases. If I understood all the relationships better, I might be able to decide this better.
class Application {
constructor(options = {}) {
return new User(options);
}
}
class User {
constructor(options) {
this._options = options;
this.state = new StateManager(this);
this.job = new Job(this);
this.init();
}
init() {
this.state.onPreload = this._options.preload.bind(this);
this.state.preload();
}
}
class Job {
constructor(user) {
this.user = user;
}
changeTitle(title) {
this.user.jobTitle = title;
}
}
class StateManager {
constructor(user) {
this.user = user;
this.onPreload = null;
}
preload() {
this.onPreload();
}
}
const options = {
preload: preload
};
const app = new Application(options);
console.log(app.jobTitle);
function preload() {
this.job.changeTitle('CEO');
}

Force the use of a factory

I have a class where I want a simple factory method:
class GTree{
public static createNode(){
return new GNode();
}
}
This means that I don't want to allow the consumer to immediately instantiate the GNode.
How do I properly implement this?
Obviously I can't do:
class GNode{
constructor(){
throw TypeError("This is nonsense");
}
}
Because then I can't create nodes anymore at all.
How do I force using the factory?
Here's a simpler scheme than my earlier comments. Just define the GNode class in a private (but shared) scope and thus that's the only place the constructor can be called from and also reset the .constructor property so it doesn't leak out:
const GTree = (function() {
class GNode {
constructor() {
}
someOtherMethod() {
console.log("someOtherMethod");
}
}
// reset public .constructor
GNode.prototype.constructor = function() {
throw new Error("Can't call GNode constructor directly");
};
class GTree {
constructor() {
this.nodes = [];
}
createNode() {
let node = new GNode();
this.nodes.push(node);
return node;
}
get length() {
return this.nodes.length;
}
}
return GTree;
})();
let tree = new GTree();
let node1 = tree.createNode();
let node2 = tree.createNode();
node1.someOtherMethod();
console.log(tree.length + " nodes");
You can't really do that in javascript, but you can do this:
export class GTree {
public static createNode(name: string): GNode {
return new GNodeImpl(name);
}
}
export interface GNode {
name: string;
}
class GNodeImpl implements GNode {
constructor(public name: string) {}
}
(code in playground)
Only GTree and the GNode interface are exported, meaning that it's not possible to instantiate GNodeImpl from outside the module.
I added the name property just for the example.

Using Mediator Pattern with webpack and ES6 Modules import export

I have multiple widgets written and need to communicated between them. I am trying to use the mediator pattern to do that. So I have something like below. Problem I am having is mediator is 2 different instances instead of just 1. So widget_2 is not actually subscribing to correct event/message.
I am using WebPack/Es6
How can I overcome that?
//mediator.js
//ref: https://github.com/HenriqueLimas/mediator-pattern-es6/blob/master/src/mediator.js
//app.js
import Widget_1 from './widget_1.js';
import Widget_2 from './widget_2.js';
new widget_1 = new Widget_1();
new widget_2 = new Widget_2();
widget_1.run();
widget_2.run();
//widget_1.js
import Mediator from './mediator.js';
const mediator = new Mediator();
export default class Widget_1 {
constructor() {
}
run() {
mediator.publish('widget1', 'hello there I am widget 1');
}
}
//widget_2.js
import Mediator from './mediator.js';
const mediator = new Mediator();
export default class Widget_2 {
constructor() {
}
run() {
mediator.subscribe('widget1', function(message) {
console.log('widget 1 says:' + message);
});
}
}
If you make your mediator a singleton - the same object will by definition be shared anywhere you use it. This modification could look smth like this.
'use strict';
class Mediator {
constructor() {
this.channels = {};
}
subscribe(channel, fn) {
if (!this.channels[channel]) this.channels[channel] = [];
this.channels[channel].push({
context: this,
callback: fn
});
}
publish(channel) {
if (!this.channels[channel]) return false;
var args = Array.prototype.slice.call(arguments, 1);
this.channels[channel].forEach(function(subscription) {
subscription.callback.apply(subscription.context, args);
});
return this;
}
installTo(obj) {
obj.channels = {};
obj.publish = this.publish;
obj.subscribe = this.subscribe;
}
}
var mediator = new Mediator();
export mediator;
But then you don't really need a es6 class here, as you will be using it only once to create a new object.

Categories