I'm using ES6 with the Webpack es6-transpiler per my article here: http://www.railsonmaui.com/blog/2014/10/02/integrating-webpack-and-the-es6-transpiler-into-an-existing-rails-project/
Does it make any sense to convert two Singleton objects to use ES6 Classes?
import { CHANGE_EVENT } from "../constants/Constants";
var EventEmitter = require('events').EventEmitter;
var merge = require('react/lib/merge');
var _flash = null;
var BaseStore = merge(EventEmitter.prototype, {
emitChange: function() {
this.emit(CHANGE_EVENT);
},
/**
* #param {function} callback
*/
addChangeListener: function(callback) {
this.on(CHANGE_EVENT, callback);
},
/**
* #param {function} callback
*/
removeChangeListener: function(callback) {
this.removeListener(CHANGE_EVENT, callback);
},
getFlash: function() {
return _flash;
},
setFlash: function(flash) {
_flash = flash;
}
});
export { BaseStore };
This is file ManagerProducts.jsx that has a singleton that should extend from BaseStore.
/**
* Client side store of the manager_product resource
*/
import { BaseStore } from "./BaseStore";
import { AppDispatcher } from '../dispatcher/AppDispatcher';
import { ActionTypes } from '../constants/Constants';
import { WebAPIUtils } from '../utils/WebAPIUtils';
import { Util } from "../utils/Util";
var merge = require('react/lib/merge');
var _managerProducts = [];
var receiveAllDataError = function(action) {
console.log("receiveAllDataError %j", action);
WebAPIUtils.logAjaxError(action.xhr, action.status, action.err);
};
var ManagerProductStore = merge(BaseStore, {
getAll: function() {
return _managerProducts;
}
});
var receiveAllDataSuccess = function(action) {
_managerProducts = action.data.managerProducts;
//ManagerProductStore.setFlash({ message: "Manager Product data loaded"});
};
ManagerProductStore.dispatchToken = AppDispatcher.register(function(payload) {
var action = payload.action;
if (Util.blank(action.type)) { throw `Invalid action, payload ${JSON.stringify(payload)}`; }
switch(action.type) {
case ActionTypes.RECEIVE_ALL_DATA_SUCCESS:
receiveAllDataSuccess(action);
break;
case ActionTypes.RECEIVE_ALL_DATA_ERROR:
receiveAllDataError(action);
break;
default:
return true;
}
ManagerProductStore.emitChange();
return true;
});
export { ManagerProductStore };
No. Makes no sense.
Here's a really simple example of a singleton object in es6:
let appState = {};
export default appState;
If you really want to use a class in your singleton approach, I would recommend against using "static" as it more confusing than good for a singleton at least for JS and instead return the instance of the class as a singleton like so...
class SomeClassUsedOnlyAsASingleton {
// implementation
}
export default new SomeClassUsedOnlyAsASingleton();
This way you can still use all the class things you like that JavaScript offers but it will reduce the confusion as IMO static isn't fully supported in JavaScript classes anyway as it is in typed languages such as c# or Java as it only supports static methods unless you just fake it and attach them directly to a class (at the time of this writing).
I'd argue that singletons (classes that manage their own singleton lifetime) are unnecessary in any language. That is not to say that singleton lifetime is not useful, just that I prefer that something other than the class manage the lifetime of an object, like a DI container.
That being said, the singleton pattern CAN be applied to JavaScript classes, borrowing the "SingletonEnforcer" pattern that was used in ActionScript. I can see wanting to do something like this when porting an existing code base that uses singletons into ES6.
In this case, the idea is that you make a private (via an un exposed Symbol) static singleton instance, with a public static instance getter. You then restrict the constructor to something that has access to a special singletonEnforcer symbol that is not exposed outside of the module. That way, the constructor fails if anyone other than the singleton tries to "new" it up. It would look something like this:
const singleton = Symbol();
const singletonEnforcer = Symbol()
class SingletonTest {
constructor(enforcer) {
if(enforcer != singletonEnforcer) throw "Cannot construct singleton";
}
static get instance() {
if(!this[singleton]) {
this[singleton] = new SingletonTest(singletonEnforcer);
}
return this[singleton];
}
}
export default SingletonTest
Then you can use it like any other singleton:
import SingletonTest from 'singleton-test';
const instance = SingletonTest.instance;
I had to do the same so here is a simple and direct way of doing a singleton,
curtsy to singleton-classes-in-es6
(original link http://amanvirk.me/singleton-classes-in-es6/)
let instance = null;
class Cache{
constructor() {
if(!instance){
instance = this;
}
// to test whether we have singleton or not
this.time = new Date()
return instance;
}
}
let cache = new Cache()
console.log(cache.time);
setTimeout(function(){
let cache = new Cache();
console.log(cache.time);
},4000);
Both console.log calls should print the same cache.time (Singleton)
In order to create Singleton pattern use a single instance with ES6 classes;
'use strict';
import EventEmitter from 'events';
class Single extends EventEmitter {
constructor() {
this.state = {};
}
getState() {
return this.state;
}
}
export default let single = new Single();
Update: According to #Bergi explanation, below one is not a valid argument.
This works because of (refer to Steven)
> If I understand CommonJS + the browser implementations correctly, the
> output of a module is cached, so export default new MyClass() will
> result in something that behaves as a singleton (only a single
> instance of this class will ever exist per process/client depending on
> env it's running in).
You can find an example here ES6 Singleton.
Note: This pattern is using in Flux Dispacher
Flux: www.npmjs.com/package/flux
Dispacher Example: github.com/facebook/flux/blob/master/examples/flux-todomvc/js/dispatcher/AppDispatcher.js#L16
Singleton class
class SingletonClass {
constructor( name = "", age = 0 ) {
if ( !this.constructor.instance ) {
this.constructor.instance = this;
this.name = name;
this.age = age;
}
return this.constructor.instance;
}
getName() {
return this.name;
}
getAge() {
return this.age;
}
}
const instanceOne = new SingletonClass( "One", 25 );
const instanceTwo = new SingletonClass( "Two", 44 );
console.log( `Name of instanceOne is "${instanceOne.getName()}"` );
console.log( `Name of instanceTwo is "${instanceTwo.getName()}"` );
Related
I'm working on a (pseudo) game engine to be used with Foundry.js. The intention was to create a scaffolding that can be reused to quickly create different game systems. To make the final product easier to read and troubleshoot, I decided to use Node.js and a very modular system. (I'm not sure if any more context is needed, but please ask if it is.)
Is there any obvious issue with my implementation?
Note: I'm an 2nd year medical student and self-taught over the past summer --> a lot of what I do might look weird so...
Note: the file and object names were removed, along with extemporaneous code. I added some fillers so that the general idea would still be conveyed
core file: singleton which contains all engine modules within its instance
const Module_A = requires('./modules/module_a.js');
const Module_B = requires('./modules/module_b.js')
module.exports = (function(){
var instance;
function createInstance(){
var o = Object.create({
Module_A: Module_A,
Module_B: Module_B,
// etc.
})
return o;
}
return {
getInstance: function(){
if(!instance){
instance = createInstance();
}
return instance;
},
Module_A: function(){
if(!instance){
instance = createInstance();
}
return instance.Module_A;
},
Module_B: function(){
if(!instan ce){
instance = createInstance();
}
return instance.Module_B;
}
};
}())
an abstract class declaration
module.exports = (
class {
constructor(){}
static getType = function(){
return entityConfig.type;
};
static entityConfig = {
type: 'Object.(Engine.Entity)'
}
}
);
a concrete class declaration
const Entity = requires('*./entity.js');
module.exports = (
class extends Entity {
static requiredParameters = () => {throw `incomplete code at ${new Error().lineNumber}`};
static optionalParameters = () => {throw `incomplete code at ${new Error().lineNumber}`};
static docData = () => {throw `incomplete code at ${new Error().lineNumber}`};
/* ------------------------------------ */
/* CONSTRUCTOR */
/* ------------------------------------ */
constructor(){
arguments.forEach(arg => {
if(!arg.includes(requiredParameters) && !arg.includes(optionalParameters)){
throw console.error(`ERROR: unknown token; ${arg}\n${docData}`);
}
});
this._data = args.data;
this.data = this._data;
}
}
);
I am using the factory method pattern in some of my code. The problem is, some of those instances also use the same factory method pattern. This creates circular dependencies and I can't think of a way of removing them. Let me give an example:
// factoryMethod.ts
import Instance1 from './Instance1';
import Instance2 from './Instance2';
import Instance3 from './Instance3';
import { Instance, InstanceName } from './Instance';
export const getInstanceByName = (
instanceName: InstanceName
): Instance => {
switch (instanceName) {
case 'Instance1':
return Instance1;
case 'Instance2':
return Instance2;
case 'Instance3':
return Instance3;
default:
throw new Error();
}
};
// extremelyHelpfulUtilityFunction.ts
import { getInstanceByName } from './factoryMethod';
export const extremelyHelpfulUtilityFunction = (instanceName: InstanceName): number => {
// Imagine this was an extremely helpful utility function
return getInstanceByName(instanceName).num
}
// Instance.ts
export interface Instance {
doSomething: () => number;
num: number;
}
export type InstanceName = 'Instance1' | 'Instance2' | 'Instance3';
// Instance1.ts
import { extremelyHelpfulUtilityFunction } from './extremelyHelpfulUtilityFunction';
const i: Instance = {
doSomething: (): number => {
return extremelyHelpfulUtilityFunction('Instance2') + extremelyHelpfulUtilityFunction('Instance3'); // circular dependency
},
}
export default i;
// Other instances defined below, you get the idea.
I'm using rollup to turn this into a single JavaScript file, and when I do, it warns me that I have a circular dependency. I want to get rid of this warning. I realize the code will still function, but I don't want the warning there. How can I modify this code so that InstanceX can get InstanceY without it being a circular dependency?
IMO the problem is that extremelyHelpfulUtilityFunction has to know getInstanceByName, while the result of this factory could always be known in advance by the caller and the desired value passed as argument to the helper.
I would propose
// Instance1.ts
const instance1: Instance = {
doSomething: (): number => {
return (new Instance2()).toNum() + (new Instance3()).toNum()
},
}
with toNum defined in Instance.ts and overridden in its subclasses, using the helper but with proper parameters, for example
// Instance2.ts
const instance2: Instance = {
doSomething: ...,
toNum: (): number => {
return extremelyHelpfulUtilityFunction(1234)
}
}
where you would use this.num instead of 1234 if you declared a proper class for Instance2 instead of this object, like
// Instance2.ts
class Instance2 extends Instance {
num = 1234;
doSomething: ...
toNum(): number {
return extremelyHelpfulUtilityFunction(this.num)
}
}
export default new Instance2();
I am trying to figure out testing with Jest for my MobX stores.
I am using Mobx, React, and Jest.
class ConfigStore {
constructor(RootStore) {
this.rootStore = RootStore;
this.config = {};
}
}
class DataStore {
constructor(RootStore) {
this.config = RootStore.config;
}
}
class UIStore {
constructor(RootStore) {
this.config = RootStore.config;
this.data = RootStore.data;
}
}
class RootStore {
constructor() {
this.config = new ConfigStore(this);
this.ui = new UIStore(this);
this.data = new DataStore(this);
}
}
Did I set my stores up correctly?
If so, what is the best way to test the stores before they get passed to Provider?
Your question is very unclear. What exactly do you want to test about these stores in unit tests? You can't really test data itself.
My suggestions:
link to stores
instead of using setting a single property just keep the whole store:
class DataStore {
constructor(RootStore) {
this.configStore = RootStore;
}
}
this way you can besure properties are always properly updated and observed.
if you want you can always have property on your lower level stores:
class DataStore {
constructor(RootStore) {
this.configStore = RootStore;
}
get config() {
return this.configStore.config;
}
}
Abstract
if you use typescript abstract your stores with interfaces so the stores are way easilier tested:
class DataStore {
constructor(store: IConfigStore) {
this.configStore = store;
}
}
interface IConfigStore {
config: Config;
}
Use a repository pattern
For every store make a repository injectable so that all api calls done by the store are actually done in this repository:
class RootStore {
constructor(repository) {
this.repostiory = repository;
this.config = new ConfigStore(this);
this.ui = new UIStore(this);
this.data = new DataStore(this);
this.initializeData();
}
async initializeData(){
this.config = await this.repository.getConfig();
}
}
This way you can easily mock the repository to give static date so you dont need to do any api calls.
Keep your react components pure
The react components that you really want to unit test. make sure they dont use mobx stores directly but you use the inject() function instead to make a second class: https://github.com/mobxjs/mobx-react#inject-as-function
this way your components are way easilier testable and useable stand alone:
const PureReactComponent = ({ name }) => <h1>{name}</h1>
const SimpleMobxComponent = inject(stores => ({
name: stores.userStore.name
}))(PureReactComponent)
// usage:
render() {
return <SimpleMobxComponent/>
}
I see patterns which make use of a singleton pattern using ES6 classes and I am wondering why I would use them as opposed to just instantiating the class at the bottom of the file and exporting the instance. Is there some kind of negative drawback to doing this? For example:
ES6 Exporting Instance:
import Constants from '../constants';
class _API {
constructor() {
this.url = Constants.API_URL;
}
getCities() {
return fetch(this.url, { method: 'get' })
.then(response => response.json());
}
}
const API = new _API();
export default API;
Usage:
import API from './services/api-service'
What is the difference from using the following Singleton pattern? Are there any reasons for using one from the other? Im actually more curious to know if the first example I gave can have issues that I am not aware of.
Singleton Pattern:
import Constants from '../constants';
let instance = null;
class API {
constructor() {
if(!instance){
instance = this;
}
this.url = Constants.API_URL;
return instance;
}
getCities() {
return fetch(this.url, { method: 'get' })
.then(response => response.json());
}
}
export default API;
Usage:
import API from './services/api-service';
let api = new API()
I would recommend neither. This is totally overcomplicated. If you only need one object, do not use the class syntax! Just go for
import Constants from '../constants';
export default {
url: Constants.API_URL,
getCities() {
return fetch(this.url, { method: 'get' }).then(response => response.json());
}
};
import API from './services/api-service'
or even simpler
import Constants from '../constants';
export const url = Constants.API_URL;
export function getCities() {
return fetch(url, { method: 'get' }).then(response => response.json());
}
import * as API from './services/api-service'
The difference is if you want to test things.
Say you have api.spec.js test file. And that your API thingy has one dependency, like those Constants.
Specifically, constructor in both your versions takes one parameter, your Constants import.
So your constructor looks like this:
class API {
constructor(constants) {
this.API_URL = constants.API_URL;
}
...
}
// single-instance method first
import API from './api';
describe('Single Instance', () => {
it('should take Constants as parameter', () => {
const mockConstants = {
API_URL: "fake_url"
}
const api = new API(mockConstants); // all good, you provided mock here.
});
});
Now, with exporting instance, there's no mocking.
import API from './api';
describe('Singleton', () => {
it('should let us mock the constants somehow', () => {
const mockConstants = {
API_URL: "fake_url"
}
// erm... now what?
});
});
With instantiated object exported, you can't (easily and sanely) change its behavior.
Both are different ways.
Exporting a class like as below
const APIobj = new _API();
export default APIobj; //shortcut=> export new _API()
and then importing like as below in multiple files would point to same instance and a way of creating Singleton pattern.
import APIobj from './services/api-service'
Whereas the other way of exporting the class directly is not singleton as in the file where we are importing we need to new up the class and this will create a separate instance for each newing up
Exporting class only:
export default API;
Importing class and newing up
import API from './services/api-service';
let api = new API()
Another reason to use Singleton Pattern is in some frameworks (like Polymer 1.0) you can't use export syntax.
That's why second option (Singleton pattern) is more useful, for me.
Hope it helps.
Maybe I'm late, because this question is written in 2018, but it still appear in the top of result page when search for js singleton class and I think that it still not have the right answer even if the others ways works. but don't create a class instance.
And this is my way to create a JS singleton class:
class TestClass {
static getInstance(dev = true) {
if (!TestClass.instance) {
console.log('Creating new instance');
Object.defineProperty(TestClass, 'instance', {
value: new TestClass(dev),
writable : false,
enumerable : true,
configurable : false
});
} else {
console.log('Instance already exist');
}
return TestClass.instance;
}
random;
constructor() {
this.random = Math.floor(Math.random() * 99999);
}
}
const instance1 = TestClass.getInstance();
console.log(`The value of random var of instance1 is: ${instance1.random}`);
const instance2 = TestClass.getInstance();
console.log(`The value of random var of instance2 is: ${instance2.random}`);
And this is the result of execution of this code.
Creating new instance
The value of random var of instance1 is: 14929
Instance already exist
The value of random var of instance2 is: 14929
Hope this can help someone
I found a lot of examples and also tried myself to split a module into several files. So I get that one, very handy. But it's also practical sometimes to split a class for the same reason. Say I have a couple of methods and I don't want to cram everything into one long file.
I'm looking for something similar to the partial declaration in C#.
Lately I use this pattern:
// file class.ts
import { getValue, setValue } from "./methods";
class BigClass {
public getValue = getValue;
public setValue = setValue;
protected value = "a-value";
}
// file methods.ts
import { BigClass } from "./class";
function getValue(this: BigClass) {
return this.value;
}
function setValue(this: BigClass, value: string ) {
this.value = value;
}
This way we can put methods in a seperate file. Now there is some circular dependency thing going on here. The file class.ts imports from methods.ts and methods.ts imports from class.ts. This may seem scary, but this is not a problem. As long as the code execution is not circular everything is fine and in this case the methods.ts file is not executing any code from the class.ts file. NP!
You could also use it with a generic class like this:
class BigClass<T> {
public getValue = getValue;
public setValue = setValue;
protected value?: T;
}
function getValue<T>(this: BigClass<T>) {
return this.value;
}
function setValue<T>(this: BigClass<T>, value: T) {
this.value = value;
}
You can't.
There was a feature request to implement partial classes, first on CodePlex and later on GitHub, but on 2017-04-04 it was declared out-of-scope. A number of reasons are given, the main takeaway seems to be that they want to avoid deviating from ES6 as much as possible:
TypeScript already has too many TS-specific class features [...] Adding yet another TS-specific class feature is another straw on the camel's back that we should avoid if we can. [...] So if there's some scenario that really knocks it out of the park for adding partial classes, then that scenario ought to be able to justify itself through the TC39 process.
I use this (works in typescript 2.2.2):
class BigClass implements BigClassPartOne, BigClassPartTwo {
// only public members are accessible in the
// class parts!
constructor(public secret: string) { }
// this is ugly-ish, but it works!
methodOne = BigClassPartOne.prototype.methodOne;
methodTwo = BigClassPartTwo.prototype.methodTwo;
}
class BigClassPartOne {
methodOne(this: BigClass) {
return this.methodTwo();
}
}
class BigClassPartTwo {
methodTwo(this: BigClass) {
return this.secret;
}
}
I use plain subclassing when converting large old multi file javascript classes which use 'prototype' into multiple typescript files:
bigclassbase.ts:
class BigClassBase {
methodOne() {
return 1;
}
}
export { BigClassBase }
bigclass.ts:
import { BigClassBase } from './bigclassbase'
class BigClass extends BigClassBase {
methodTwo() {
return 2;
}
}
You can import BigClass in any other typescript file.
A modified version of proposed pattern.
// temp.ts contents
import {getValue, setValue} from "./temp2";
export class BigClass {
// #ts-ignore - to ignore TS2564: Property 'getValue' has no initializer and is not definitely assigned in the constructor.
public getValue:typeof getValue;
// #ts-ignore - to ignore TS2564: Property 'setValue' has no initializer and is not definitely assigned in the constructor.
public setValue:typeof setValue;
protected value = "a-value";
}
BigClass.prototype.getValue = getValue;
BigClass.prototype.setValue = setValue;
//======================================================================
// temp2.ts contents
import { BigClass } from "./temp";
export function getValue(this: BigClass) {
return this.value;
}
export function setValue(this: BigClass, value: string ) {
this.value = value;
}
Pros
Doesn't create additional fields in class instances so there is no overhead: in construction, destruction, no additional memory used. Field declations in typescript are only used for typings here, they don't create fields in Javascript runtime.
Intellisence is OK (tested in Webstorm)
Cons
ts-ignore is needed
Uglier syntax than #Elmer's answer
The rest properties of solutions are same.
Modules let you extend a typescript class from another file:
user.ts
export class User {
name: string;
}
import './user-talk';
user-talk.ts
import { User } from './user';
class UserTalk {
talk (this:User) {
console.log(`${this.name} says relax`);
}
}
User.prototype.sayHi = UserTalk.prototype.sayHi;
declare module './user' {
interface User extends UserTalk { }
}
Usage:
import { User } from './user';
const u = new User();
u.name = 'Frankie';
u.talk();
> Frankie says relax
If you have a lot of methods, you might try this:
// user.ts
export class User {
static extend (cls:any) {
for (const key of Object.getOwnPropertyNames(cls.prototype)) {
if (key !== 'constructor') {
this.prototype[key] = cls.prototype[key];
}
}
}
...
}
// user-talk.ts
...
User.extend(UserTalk);
Or add the subclass to the prototype chain:
...
static extend (cls:any) {
let prototype:any = this;
while (true) {
const next = prototype.prototype.__proto__;
if (next === Object.prototype) break;
prototype = next;
}
prototype.prototype.__proto__ = cls.prototype;
}
You can use multi file namespaces.
Validation.ts:
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
}
LettersOnlyValidator.ts (uses the StringValidator from above):
/// <reference path="Validation.ts" />
namespace Validation {
const lettersRegexp = /^[A-Za-z]+$/;
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
}
}
}
Test.ts (uses both StringValidator and LettersOnlyValidator from above):
/// <reference path="Validation.ts" />
/// <reference path="LettersOnlyValidator.ts" />
// Some samples to try
let strings = ["Hello", "101"];
// Validators to use
let validators: { [s: string]: Validation.StringValidator; } = {};
validators["Letters only"] = new Validation.LettersOnlyValidator();
Why not just use Function.call that js already comes with.
class-a.ts
Class ClassA {
bitten: false;
constructor() {
console.log("Bitten: ", this.bitten);
}
biteMe = () => biteMe.call(this);
}
and in other file bite-me.ts
export function biteMe(this: ClassA) {
// do some stuff
// here this refers to ClassA.prototype
this.bitten = true;
console.log("Bitten: ", this.bitten);
}
// using it
const state = new ClassA();
// Bitten: false
state.biteMe();
// Bitten: true
For more information have a look at the definition of Function.call
Personally I use #partial decorator acts as a simplified syntax that may help divide functionality of a single class into multiple 🍬🍬🍬 class files ... https://github.com/mustafah/partials
// Declaration file
class BigClass {
declare method: (n: number, s: string) => string;
}
// Implementation file
BigClass.prototype.method = function (this: BigClass, n: number, s: string) {
return '';
}
The downside of this approach is that it is possible to declare a method but to forget to actually add its implementation.
We can extend class methods gradually with prototype and Interface definition:
import login from './login';
import staffMe from './staff-me';
interface StaffAPI {
login(this: StaffAPI, args: LoginArgs): Promise<boolean>;
staffsMe(this: StaffAPI): Promise<StaffsMeResponse>;
}
class StaffAPI {
// class body
}
StaffAPI.prototype.login = login;
StaffAPI.prototype.staffsMe = staffsMe;
export default StaffAPI;
This is how i have been doing it
Mixins approach
To add to #Elmer's solution, I added following to get it to work in separate file.
some-function-service-helper.ts
import { SomeFunctionService } from "./some-function-service";
export function calculateValue1(this: SomeFunctionService) {
...
}
some-function-service.ts
import * as helper from './some-function-service-helper';
#Injectable({
providedIn: 'root'
})
export class SomeFunctionService {
calculateValue1 = helper.calculateValue1; // helper function delcaration used for getNewItem
public getNewItem() {
var one = this.calculateValue1();
}