在Typescript/ES6中,单例模式的正确方式是什么?

What is the right way for Singleton pattern in Typescript/ES6?

本文关键字:方式 是什么 单例模式 Typescript ES6      更新时间:2023-09-26
class Foo{
}
var instance: Foo;
export function getFooInstance(){
    /* logic */
}

export class Foo{
    private static _instance;
    private constructor(){};
    public getInstance(){/* logic */}
}
// Use it like this
Foo.getInstance()

我想确保只有一个方法实例的对象?除此之外,还有什么建议吗?

如果你想在类中使用getter,那么它需要是静态的:

export class Foo{
    private static _instance;
    private constructor(){};
    public static getInstance(){/* logic */}
}

问题是,虽然编译器将强制这种私有可见性,但在运行时仍然有可能绕过它,即使是无意的,如果例如有人直接从javascript中使用它。

如果你使用模块/命名空间强制它,那么你可以完全隐藏它:

使用模块:

export interface IFoo {}
class Foo implements IFoo {}
var instance: Foo;
export function getFooInstance(): IFoo {
    /* logic */
    return instance;
}

这是你的代码,我只是添加了IFoo接口(这也是导出的),所以谁得到一个实例将知道接口,而不是类。

使用命名空间:

namespace Foo {
    export interface IFoo {}
    class FooClass implements IFoo {}
    const instance = new FooClass();
    export function getInstance(): IFoo {
        return instance;
    }
}

在JS和TypeScript中,如果你真的只想要一个实例,为什么不通过导出对象字面量来强制使用语言本身呢?

const Foo = {
  doSomething() {
  }
}
export default Foo;

在我看来,这是遵循KISS,最少的样板文件,任何人都没有办法创建多个实例。

也就是说,你也可以直接导出函数。记住一个模块本身可以作为你的单例。

export function doSomething() {
}

然后导入,并希望将其视为对象,您可以使用import *。如果函数确实属于对象,并且不是所有的无状态静态函数,我更喜欢第一种方法。

import * as Foo from './Foo';
Foo.doSomething();

这取决于是否有机会为单例类创建一个新的实例。在最后一种情况下,可以省略getInstance,类构造函数可以充当单例工厂:

class Foo {
    private static _instance;
    constructor(...args) {
        if (Foo._instance) {
            return Foo._instance;
        }
        // if Foo extends non-singleton class or whatever,
        // super(...args);
        Foo._instance = this;
    };
}

同样的事情也可以用任意类的装饰器来完成,比如:

@singleton
class Foo { ... }

由于TypeScript装饰器存在一些类型问题,应该在singleton装饰器中使用自定义继承代码,而不是Singleton extends Class:

function singleton(Class) {
    function extend(sub, sup) {
        for (var prop in sup)
            if (sup.hasOwnProperty(prop))
                sub[prop] = sup[prop];
        function __() {
            this.constructor = sub;
        }
        __.prototype = sup.prototype;
        sub.prototype = new __();
    };
    const Singleton = <any>function (...args) {
        if (Singleton._instance) {
            return Singleton._instance;
        }
        Class.apply(this, args);
        Singleton._instance = this;
    }
    extend(Singleton, Class);
    return Singleton;
}

可能会影响输入,但语法保持整洁。