【问题标题】:Converting Singleton JS objects to use ES6 classes将 Singleton JS 对象转换为使用 ES6 类
【发布时间】:2014-11-30 01:59:49
【问题描述】:

我在这里的文章中使用 ES6 和 Webpack es6-transpiler:http://www.railsonmaui.com/blog/2014/10/02/integrating-webpack-and-the-es6-transpiler-into-an-existing-rails-project/

将两个 Singleton 对象转换为使用 ES6 类是否有意义?

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

这是文件 ManagerProducts.jsx,它有一个应该从 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 };

【问题讨论】:

  • 没有。如果您有单例,则不需要 JavaScript 中的类。类用于构造多个实例。
  • @bergi,我就是这么想的。但只是检查是否有我遗漏的东西。

标签: javascript ecmascript-6 ecmascript-harmony


【解决方案1】:

没有。没有意义。

这是一个非常简单的 es6 中的单例对象示例:

let appState = {};
export default appState;

如果你真的想在你的单例方法中使用一个类,我建议不要使用“静态”,因为它至少对于 JS 来说对于单例来说比好的更令人困惑,而是像这样将类的实例作为单例返回...

class SomeClassUsedOnlyAsASingleton {
  // implementation
}

export default new SomeClassUsedOnlyAsASingleton();

这样你仍然可以使用 JavaScript 提供的所有你喜欢的类的东西,但它会减少混乱,因为 JavaScript 类并不完全支持 IMO 静态,因为它只在 c# 或 Java 等类型化语言中支持静态方法,除非您只是伪造它并将它们直接附加到一个类(在撰写本文时)。

【讨论】:

  • 或者只是一行export default let appState = {}; :-)
  • @Jason:谢谢,你真的让我大开眼界。我也尝试定义一个单例类,但是拥有一个对象要简单得多。你甚至可以在这个对象中定义函数,我看不出有什么缺点。
  • let appState = { func: function() { // blah blah blah } };
  • @dejakob: es6 obj 字面量?我相信有人可以指出不这样做的理由,但是:export default { __proto__: new Inherited(), newMethod() {} }
  • 你把课放在哪里?构造函数在哪里?为什么不举个例子?
【解决方案2】:

我认为单例(管理自己的单例生命周期的类)在任何语言中都是不必要的。这并不是说单例生命周期没有用,只是我更喜欢类以外的东西来管理对象的生命周期,比如 DI 容器。

话虽如此,单例模式可以应用于 JavaScript 类,借用 ActionScript 中使用的“SingletonEnforcer”模式。在将使用单例的现有代码库移植到 ES6 时,我可以看到想要做这样的事情。

在这种情况下,我们的想法是创建一个私有(通过未公开的符号)静态 singleton 实例,并使用公共静态 instance getter。然后,您将构造函数限制为可以访问未在模块外部公开的特殊 singletonEnforcer 符号的东西。这样,如果除了单例之外的任何人试图“新建”它,构造函数就会失败。它看起来像这样:

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

然后你可以像使用任何其他单例一样使用它:

import SingletonTest from 'singleton-test';
const instance = SingletonTest.instance;

【讨论】:

  • amanvirk.me/singleton-classes-in-es6 是更好的方法,因为它管理构造函数本身的状态
  • @AmanVirk 如果您要扩展类,该方法是否有效?
  • IMO 在实例 getter 中使用它有点奇怪。它不能引用类本身,因为它是一个静态方法。我宁愿把``` const instance = null; ``` 在类外然后在代码中使用实例。无论如何,处理器都会将所有这些都放在一个闭包中,因此它最终将是一个私有变量。除此之外,这似乎是一个很好的解决方案!
  • @dejakob 在静态方法中引用 this 将引用 CLASS(确切地说是原型),而不是对象。通过使用在类外定义的符号,我们实质上是在创建一个私有类成员(与私有实例成员不同)。这样做的唯一原因是维护类语义。但是,我强烈倾向于不使用单例。我更喜欢容器对生命负责。我不喜欢班级对他们的一生负责。总感觉不对。
  • @Darlesson 是的,我认为这就是我在实际案例中会做的事情。这是“经典”单例模式的示例,其中类本身被导出,“实例”是类级别的属性。我从来没有这样写代码。
【解决方案3】:

我也必须这样做,所以这里有一个简单直接的方法来做单例, 向singleton-classes-in-es6行屈膝礼

(原链接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);

两个console.log 调用应该打印相同的cache.time(单例)

【讨论】:

  • @shinzou 是什么让您认为它不适用于扩展类?所有子类都将返回相同的单例实例,这就是单例的工作方式。
【解决方案4】:

为了创建单例模式使用带有 ES6 类的单个实例;

'use strict';

import EventEmitter from 'events';

class Single extends EventEmitter {
    constructor() {
        this.state = {};
    }

    getState() {
        return this.state;
    }

}

export default let single = new Single();

更新:根据@Bergi 的解释,以下一个不是有效的参数。

这是因为(参考Steven

> 如果我正确理解 CommonJS + 浏览器实现, > 模块的输出被缓存,因此 export default new MyClass() 将 > 导致某些行为表现为单例(只有一个 > 此类的实例将永远存在于每个进程/客户端,具体取决于 > env 它正在运行)。

你可以在这里找到一个例子ES6 Singleton

注意:此模式在 Flux Dispacher 中使用

通量:www.npmjs.com/package/flux

调度程序示例github.com/facebook/flux/blob/master/examples/flux-todomvc/js/dispatcher/AppDispatcher.js#L16

【讨论】:

  • 不要这样做!当您不想上课时,不应该使用class 语法!即使在 Flux 中使用了这种模式,也不一定是好的模式。顺便说一句,那句话是完全错误的,使用new singleton.constructor 创建第二个实例很简单。
  • @Bergi 假设 Dispatcher 是一个类实现,我想扩展它的功能。因此,我使用 JS ES6 extends 创建了另一个名为 AppDispacher 的类,并且需要该类的单个实例。那我必须使用this approach吗?但是 Jason 说“毫无意义”。那么,你的方法是什么?我不明白一件事。你把这个答案弄错了。但它在 Facebook 这样的公司中工作和使用。 :)
  • 好吧,如果您在应用程序中使用多个调度程序,这可能是有道理的,但即使您的模块只导出一个实例,我也不会再将其称为“单例”。跨度>
  • @Bergi 据我了解,这种模式不能称为“单例”。但它允许拥有一个可以在多个地方(在应用程序中)访问的单个实例,并且使用它没有任何问题。
  • 虽然技术上没有必要,但我喜欢这种模式,因为它可以让你使用“扩展”
【解决方案5】:

单例类

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-08-05
    • 1970-01-01
    • 1970-01-01
    • 2018-08-25
    • 2014-10-02
    • 2017-04-06
    • 2022-01-12
    相关资源
    最近更新 更多