【问题标题】:ES6 Singleton vs Instantiating a Class onceES6 Singleton vs 实例化一个类一次
【发布时间】:2018-06-30 04:44:39
【问题描述】:

我看到了使用 ES6 类的单例模式的模式,我想知道为什么我会使用它们而不是仅在文件底部实例化类并导出实例。这样做有什么负面的缺点吗?例如:

ES6 导出实例:

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;

用法:

import API from './services/api-service'

与使用以下单例模式有什么区别?是否有任何理由使用另一个?实际上,我更想知道我给出的第一个示例是否存在我不知道的问题。

单例模式:

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;

用法:

import API from './services/api-service';

let api = new API()

【问题讨论】:

    标签: javascript es6-class


    【解决方案1】:

    我都不推荐。这完全过于复杂了。如果您只需要一个对象,do not use the class syntax!去吧

    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'
    

    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'
    

    【讨论】:

    • 这是在 js 中执行此操作的正确惯用方式
    • 请注意,javascript 一直都在语言中内置了单例。我们只是不称它们为单例,我们称它们为对象字面量。因此,每当您需要一个对象的单个实例时,js 程序员都会自动创建一个对象字面量。在js中,很多其他语言中的“设计模式”都是内置语法。
    • 我会说如果您想将提取作为依赖项注入,这是不合适的,因此更容易测试。
    • @CésarAlberca OP 没有使用依赖注入,所以我没有考虑这一点。你仍然不需要class,模块导入或工厂函数应该足以使fetch 可模拟。
    • @codewise 答案中的link 解释了为什么单例对象应避免使用class 语法。
    【解决方案2】:

    不同之处在于你是否想测试。

    假设你有api.spec.js 测试文件。而且你的 API 有一个依赖项,比如那些常量。

    具体来说,您的两个版本中的构造函数都采用一个参数,即您的 Constants 导入。

    所以你的构造函数看起来像这样:

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

    现在,通过导出实例,没有模拟了。

    import API from './api';
    describe('Singleton', () => {
        it('should let us mock the constants somehow', () => {
            const mockConstants = {
                API_URL: "fake_url"
            }
            // erm... now what?
        });
    });
    

    导出实例化对象后,您无法(轻松而明智地)更改其行为。

    【讨论】:

    • 无论出于何种原因,Javascript 开发人员都倾向于通过 import 对所有依赖项进行硬编码。我同意通过构造函数传递依赖项是更好的做法,这样它a) 可测试,b) 可重用。
    • 我授予这个答案是因为它实际上回答了我最初的问题。谢谢。
    • "// 呃……现在怎么办?"为什么不? API.url = MockConstants.API_URL;对象具有所有这些实例属性/方法,无论哪个类都可以使用“this”访问。但是,突变当然会在单元测试中引起其他问题
    • @ShishirArora 问题在于这个。你有一个测试,你断言API.url === 'example.com'。都好。然后有人在您的测试之前插入此API.url === 'something else' - 您正在修改整个测试套件的API 对象,而不仅仅是一个测试实例。现在你破坏了其他测试——即使你没有(可能)破坏代码本身。
    • 这应该是所有测试服拆解操作的一部分。在开始新的测试之前,您应该清除测试留下的所有副作用。不过,并行化测试将是一个问题。
    【解决方案3】:

    两者是不同的方式。 像下面这样导出一个类

    const APIobj = new _API();
    export default APIobj;   //shortcut=> export new _API()
    

    然后像下面这样在多个文件中导入将指向同一个实例和一种创建单例模式的方法。

    import APIobj from './services/api-service'
    

    而直接导出类的另一种方式不是单例,因为在我们正在导入的文件中,我们需要新建类,这将为每个新建创建一个单独的实例 仅导出类:

    export default API;
    

    导入类和更新

    import API from './services/api-service';
    let api = new API()
    

    【讨论】:

      【解决方案4】:

      使用单例模式的另一个原因是在某些框架(如 Polymer 1.0)中,您不能使用 export 语法。
      这就是为什么第二个选项(单例模式)对我来说更有用的原因。

      希望对你有帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多