Converting Singleton JS objects to use ES6 classes

半世苍凉 提交于 2019-11-30 10:25:49

问题


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 };

回答1:


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;



回答2:


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).




回答3:


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)




回答4:


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




回答5:


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()}"` );


来源:https://stackoverflow.com/questions/26205565/converting-singleton-js-objects-to-use-es6-classes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!