如何在TypeScript中定义Singleton

maj*_*aja 119 singleton typescript

在TypeScript中为类实现Singleton模式的最佳和最方便的方法是什么?(有和没有延迟初始化).

Ale*_*lex 160

从TS 2.0开始,我们就可以在构造函数上定义可见性修饰符,所以现在我们可以像在其他语言中一样使用TypeScript中的单例.

给出的例子:

class MyClass
{
    private static _instance: MyClass;

    private constructor()
    {
        //...
    }

    public static get Instance()
    {
        // Do you need arguments? Make it a regular static method instead.
        return this._instance || (this._instance = new this());
    }
}

const myClassInstance = MyClass.Instance;
Run Code Online (Sandbox Code Playgroud)

  • 构造函数可以是私有的吗? (2认同)
  • @Expertwannabe现在可以在TS 2.0中使用:https://github.com/Microsoft/TypeScript/wiki/What's-new-in-TypeScript#private-and-protected-constructors (2认同)
  • 这是我的首选答案!谢谢. (2认同)
  • @KimchiMan如果项目曾在非打字稿环境中使用,例如导入到JS项目中,则该类将无法防止进一步实例化.它仅适用于纯TS环境,但不适用于JS库开发 (2认同)
  • @lony只需使 _Instance_ 成为一个普通的静态方法(删除“get”)即可从将参数传递给构造函数的位置获取所需的参数,该构造函数也获取所需的参数。 (2认同)

Rya*_*ugh 83

TypeScript中的Singleton类通常是反模式.您可以简单地使用命名空间.

无用的单身模式

class Singleton {
    /* ... lots of singleton logic ... */
    public someMethod() { ... }
}

// Using
var x = Singleton.getInstance();
x.someMethod();
Run Code Online (Sandbox Code Playgroud)

命名空间等价物

export namespace Singleton {
    export function someMethod() { ... }
}
// Usage
import { SingletonInstance} from "path/to/Singleton";

SingletonInstance.someMethod();
var x = SingletonInstance; // If you need to alias it for some reason
Run Code Online (Sandbox Code Playgroud)

  • 现在为什么单身人士被认为是反模式会很好?考虑这种方法http://www.codebelt.com/typescript/typescript-singleton-pattern/ (49认同)
  • 我想知道为什么TypeScript中的Singletons也被认为是一种反模式.而且如果它没有任何构造函数参数,为什么不`导出默认的新Singleton()`? (17认同)
  • 命名空间解决方案看起来更像是静态类,而不是单例 (16认同)
  • 使用命名空间作为单例的局限性在于(据我所知)它不能实现接口.你同意这个@ryan吗? (9认同)
  • 它的行为相同.在C#中,你不能传递静态类,就好像它是一个值(即好像它是一个单例类的实例),这限制了它的用处.在TypeScript中,您*可以*像实例一样传递名称空间.这就是你不需要单身人士课程的原因. (5认同)
  • 他说"单身*级别*"被认为是一种反模式,而不是"单身人士".我认为当你考虑一个类通常是为了在该类的多个实例中重用该类中的代码时,这是有道理的. (4认同)
  • `namespace` 和 `module` 意思完全一样 (2认同)
  • 考虑到类是面向对象语言中组织状态和行为的核心概念,并且这个概念由Typescript提供,因为它扩展了javascript,为我们提供了一些我们认为对组织代码有用但在javascript中缺失的概念.在所有其他语言中,如果我们想要单例,我们使用类的结构,这是事实上的规范.即使打字稿中的单例模式可能通过完全不同的结构概念(命名空间)成功实现,这是否真的值得增加概念的复杂性和模糊性? (2认同)
  • codebelt.com 的链接已损坏。我认为这是实际的链接:https://codebelt.github.io/blog/typescript/typescript-singleton-pattern/ (2认同)
  • 据我所知,现在命名空间是反模式。请参阅此 eslint 规则:https://github.com/typescript-eslint/typescript-eslint/blob/v2.34.0/packages/eslint-plugin/docs/rules/no-namespace.md (2认同)

cod*_*elt 39

我发现的最好方法是:

class SingletonClass {

    private static _instance:SingletonClass = new SingletonClass();

    private _score:number = 0;

    constructor() {
        if(SingletonClass._instance){
            throw new Error("Error: Instantiation failed: Use SingletonClass.getInstance() instead of new.");
        }
        SingletonClass._instance = this;
    }

    public static getInstance():SingletonClass
    {
        return SingletonClass._instance;
    }

    public setScore(value:number):void
    {
        this._score = value;
    }

    public getScore():number
    {
        return this._score;
    }

    public addPoints(value:number):void
    {
        this._score += value;
    }

    public removePoints(value:number):void
    {
        this._score -= value;
    }

}
Run Code Online (Sandbox Code Playgroud)

以下是您使用它的方式:

var scoreManager = SingletonClass.getInstance();
scoreManager.setScore(10);
scoreManager.addPoints(1);
scoreManager.removePoints(2);
console.log( scoreManager.getScore() );
Run Code Online (Sandbox Code Playgroud)

http://www.codebelt.com/typescript/typescript-singleton-pattern/

  • 我认为该帖子早于在TS中拥有私有构造函数的能力.https://github.com/Microsoft/TypeScript/issues/2341 (4认同)
  • 为什么不将构造函数设为私有? (3认同)

maj*_*aja 22

以下方法创建一个Singleton类,可以像传统类一样exacly使用:

class Singleton {
    private static instance: Singleton;
    //Assign "new Singleton()" here to avoid lazy initialisation

    constructor() {
        if (Singleton.instance) {
            return Singleton.instance;
        }

        this. member = 0;
        Singleton.instance = this;
    }

    member: number;
}
Run Code Online (Sandbox Code Playgroud)

每个new Singleton()操作都将返回相同的实例.然而,这可能是用户意外的.

以下示例对用户更透明,但需要使用不同的用法:

class Singleton {
    private static instance: Singleton;
    //Assign "new Singleton()" here to avoid lazy initialisation

    constructor() {
        if (Singleton.instance) {
            throw new Error("Error - use Singleton.getInstance()");
        }
        this.member = 0;
    }

    static getInstance(): Singleton {
        Singleton.instance = Singleton.instance || new Singleton();
        return Singleton.instance;
    }

    member: number;
}
Run Code Online (Sandbox Code Playgroud)

用法: var obj = Singleton.getInstance();

  • 不同意 Cody 的观点,新实例必须是新实例,否则开发人员会做出错误的假设。 (5认同)

Rom*_*ert 15

我很惊讶在这里看不到以下模式,实际上看起来非常简单.

// shout.ts
class ShoutSingleton {
  helloWorld() { return 'hi'; }
}

export let Shout = new ShoutSingleton();
Run Code Online (Sandbox Code Playgroud)

用法

import { Shout } from './shout';
Shout.helloWorld();
Run Code Online (Sandbox Code Playgroud)

  • 您还必须导出类'ShoutSingleton',错误消失. (3认同)
  • 猜猜没有什么阻止用户仅仅创建一个新的`Shout`类 (3认同)
  • “export let Shout = new ShoutSingleton();”这一解决方案的问题在于,它不是单例模式,因为它不能实现单例模式的“确保一个类只有一个实例”的目标。这是因为每次导入它时,您都会创建新实例,而不是重复使用相同的实例。简而言之,要使其成为单例,您需要确保始终返回相同的实例,而不是新的实例。通过添加“延迟初始化”可以解决此问题。 (2认同)

Dan*_*ela 8

我的解决方案:

export default class Singleton {
    private static _instance: Singleton = new Singleton();

    constructor() {
        if (Singleton._instance)
            throw new Error("Use Singleton.instance");
        Singleton._instance = this;
    }

    static get instance() {
        return Singleton._instance;
    }
}

Run Code Online (Sandbox Code Playgroud)

2021年更新

现在构造函数可以是私有的

export default class Singleton {
    private static _instance?: Singleton;

    private constructor() {
        if (Singleton._instance)
            throw new Error("Use Singleton.instance instead of new.");
        Singleton._instance = this;
    }

    static get instance() {
        return Singleton._instance ?? (Singleton._instance = new Singleton());
    }
}
Run Code Online (Sandbox Code Playgroud)

在 TS Playground 进行测试

  • 在构造函数中,您可以“返回 Modal._instance”,而不是异常。这样,如果您“新建”该类,您将获得现有对象,而不是新对象。 (3认同)

bin*_*les 7

你可以使用类表达式(我相信1.6).

var x = new (class {
    /* ... lots of singleton logic ... */
    public someMethod() { ... }
})();
Run Code Online (Sandbox Code Playgroud)

或者如果您的类需要在内部访问其类型,请使用该名称

var x = new (class Singleton {
    /* ... lots of singleton logic ... */
    public someMethod(): Singleton { ... }
})();
Run Code Online (Sandbox Code Playgroud)

另一种选择是使用一些静态成员在单例内部使用本地类

class Singleton {

    private static _instance;
    public static get instance() {

        class InternalSingleton {
            someMethod() { }

            //more singleton logic
        }

        if(!Singleton._instance) {
            Singleton._instance = new InternalSingleton();
        }

        return <InternalSingleton>Singleton._instance;
    }
}

var x = Singleton.instance;
x.someMethod();
Run Code Online (Sandbox Code Playgroud)


Fla*_*ken 6

将以下6行添加到任何类中,使其成为“ Singleton”。如果您希望通过属性而不是方法来获取实例,请使用Alex答案。

class MySingleton
{
    private constructor(){ /* ... */}
    private static _instance:MySingleton;
    public static getInstance():MySingleton
    {
        return this._instance||(this._instance = new this());
    };
}
Run Code Online (Sandbox Code Playgroud)

测试例:

var test = MySingleton.getInstance(); // will create the first instance
var test2 = MySingleton.getInstance(); // will return the first instance
alert(test === test2); // true
Run Code Online (Sandbox Code Playgroud)


san*_*nye 5

我想也许使用泛型会更好

class Singleton<T>{
    public static Instance<T>(c: {new(): T; }) : T{
        if (this._instance == null){
            this._instance = new c();
        }
        return this._instance;
    }

    private static _instance = null;
}
Run Code Online (Sandbox Code Playgroud)

如何使用

第1步

class MapManager extends Singleton<MapManager>{
     //do something
     public init():void{ //do }
}
Run Code Online (Sandbox Code Playgroud)

第2步

    MapManager.Instance(MapManager).init();
Run Code Online (Sandbox Code Playgroud)