Antworten:
Singleton-Klassen in TypeScript sind im Allgemeinen ein Anti-Pattern. Sie können stattdessen einfach Namespaces verwenden.
class Singleton {
/* ... lots of singleton logic ... */
public someMethod() { ... }
}
// Using
var x = Singleton.getInstance();
x.someMethod();
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
export default new Singleton()
?
Seit TS 2.0 können wir Sichtbarkeitsmodifikatoren für Konstruktoren definieren , sodass wir jetzt Singletons in TypeScript erstellen können, wie wir es von anderen Sprachen gewohnt sind.
Beispiel gegeben:
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;
Vielen Dank an @Drenai für den Hinweis, dass Sie beim Schreiben von Code mit dem rohen kompilierten Javascript keinen Schutz vor mehrfacher Instanziierung haben, da die Einschränkungen von TS verschwinden und der Konstruktor nicht ausgeblendet wird.
Der beste Weg, den ich gefunden habe, ist:
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;
}
}
So verwenden Sie es:
var scoreManager = SingletonClass.getInstance();
scoreManager.setScore(10);
scoreManager.addPoints(1);
scoreManager.removePoints(2);
console.log( scoreManager.getScore() );
https://codebelt.github.io/blog/typescript/typescript-singleton-pattern/
Der folgende Ansatz erstellt eine Singleton-Klasse, die genau wie eine herkömmliche Klasse verwendet werden kann:
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;
}
Jede new Singleton()
Operation gibt dieselbe Instanz zurück. Dies kann jedoch vom Benutzer unerwartet sein.
Das folgende Beispiel ist für den Benutzer transparenter, erfordert jedoch eine andere Verwendung:
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;
}
Verwendung: var obj = Singleton.getInstance();
new Class(...)
Syntax implementieren .
Ich bin überrascht, das folgende Muster hier nicht zu sehen, das eigentlich sehr einfach aussieht.
// shout.ts
class ShoutSingleton {
helloWorld() { return 'hi'; }
}
export let Shout = new ShoutSingleton();
Verwendung
import { Shout } from './shout';
Shout.helloWorld();
Shout
Sie können hierfür Klassenausdrücke verwenden (ab 1.6 glaube ich).
var x = new (class {
/* ... lots of singleton logic ... */
public someMethod() { ... }
})();
oder mit dem Namen, wenn Ihre Klasse intern auf ihren Typ zugreifen muss
var x = new (class Singleton {
/* ... lots of singleton logic ... */
public someMethod(): Singleton { ... }
})();
Eine andere Möglichkeit besteht darin, eine lokale Klasse in Ihrem Singleton mit einigen statischen Elementen zu verwenden
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();
Fügen Sie jeder Klasse die folgenden 6 Zeilen hinzu, um sie zu "Singleton" zu machen.
class MySingleton
{
private constructor(){ /* ... */}
private static _instance: MySingleton;
public static getInstance(): MySingleton
{
return this._instance || (this._instance = new this());
};
}
var test = MySingleton.getInstance(); // will create the first instance
var test2 = MySingleton.getInstance(); // will return the first instance
alert(test === test2); // true
[Bearbeiten]: Verwenden Sie die Alex-Antwort, wenn Sie die Instanz lieber über eine Eigenschaft als über eine Methode abrufen möchten.
new MySingleton()
5 mal sage? Reserviert Ihr Code eine einzelne Instanz?
Ich denke, vielleicht verwenden Generika Teig
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;
}
wie benutzt man
Schritt 1
class MapManager extends Singleton<MapManager>{
//do something
public init():void{ //do }
}
Schritt 2
MapManager.Instance(MapManager).init();
Sie können auch die Funktion Object.Freeze () verwenden . Es ist einfach und leicht:
class Singleton {
instance: any = null;
data: any = {} // store data in here
constructor() {
if (!this.instance) {
this.instance = this;
}
return this.instance
}
}
const singleton: Singleton = new Singleton();
Object.freeze(singleton);
export default singleton;
if (!this.instance)
im Konstruktor beschäftigen? Ist dies nur eine zusätzliche Vorsichtsmaßnahme, falls Sie vor dem Export mehrere Instanzen erstellt haben?
Ich habe eine neue Version davon gefunden, mit der der Typescript-Compiler völlig einverstanden ist, und ich denke, sie ist besser, weil nicht getInstance()
ständig eine Methode aufgerufen werden muss.
import express, { Application } from 'express';
export class Singleton {
// Define your props here
private _express: Application = express();
private static _instance: Singleton;
constructor() {
if (Singleton._instance) {
return Singleton._instance;
}
// You don't have an instance, so continue
// Remember, to set the _instance property
Singleton._instance = this;
}
}
Dies hat einen anderen Nachteil. Wenn Ihre Singleton
Eigenschaften vorhanden sind, gibt der Typescript-Compiler eine Anpassung aus, es sei denn, Sie initialisieren sie mit einem Wert. Aus diesem Grund habe ich eine _express
Eigenschaft in meine Beispielklasse aufgenommen, da Typescript denkt, dass sie nicht definiert wurde, es sei denn, Sie initialisieren sie mit einem Wert, auch wenn Sie sie später im Konstruktor zuweisen. Dies könnte durch Deaktivieren des strengen Modus behoben werden, aber ich ziehe es vor, wenn möglich, dies nicht zu tun. Diese Methode hat noch einen weiteren Nachteil, auf den ich hinweisen sollte, da der Konstruktor tatsächlich aufgerufen wird und jedes Mal, wenn er dies tut, eine andere Instanz technisch erstellt wird, auf die jedoch nicht zugegriffen werden kann. Dies könnte theoretisch zu Speicherlecks führen.
Dies ist wahrscheinlich der längste Prozess, um einen Singleton in Typoskript zu erstellen, aber in größeren Anwendungen hat er für mich besser funktioniert.
Zuerst benötigen Sie eine Singleton-Klasse in "./utils/Singleton.ts" :
module utils {
export class Singleton {
private _initialized: boolean;
private _setSingleton(): void {
if (this._initialized) throw Error('Singleton is already initialized.');
this._initialized = true;
}
get setSingleton() { return this._setSingleton; }
}
}
Stellen Sie sich nun vor, Sie benötigen einen Router-Singleton "./navigation/Router.ts" :
/// <reference path="../utils/Singleton.ts" />
module navigation {
class RouterClass extends utils.Singleton {
// NOTICE RouterClass extends from utils.Singleton
// and that it isn't exportable.
private _init(): void {
// This method will be your "construtor" now,
// to avoid double initialization, don't forget
// the parent class setSingleton method!.
this.setSingleton();
// Initialization stuff.
}
// Expose _init method.
get init { return this.init; }
}
// THIS IS IT!! Export a new RouterClass, that no
// one can instantiate ever again!.
export var Router: RouterClass = new RouterClass();
}
Nice!, Jetzt initialisieren oder importieren, wo immer Sie brauchen:
/// <reference path="./navigation/Router.ts" />
import router = navigation.Router;
router.init();
router.init(); // Throws error!.
Das Schöne an Singletons auf diese Weise ist, dass Sie immer noch die Schönheit von Typoskriptklassen nutzen, dass Sie eine gute Intelligenz haben, die Singleton-Logik irgendwie getrennt bleibt und bei Bedarf leicht entfernt werden kann.
Meine Lösung dafür:
export default class Modal {
private static _instance : Modal = new Modal();
constructor () {
if (Modal._instance)
throw new Error("Use Modal.instance");
Modal._instance = this;
}
static get instance () {
return Modal._instance;
}
}
return Modal._instance
. Auf diese Weise erhalten Sie, wenn Sie new
diese Klasse haben, das vorhandene Objekt, kein neues.
In Typescript muss man nicht unbedingt der new instance()
Singleton-Methode folgen . Eine importierte statische Klasse ohne Konstruktor kann genauso gut funktionieren.
Erwägen:
export class YourSingleton {
public static foo:bar;
public static initialise(_initVars:any):void {
YourSingleton.foo = _initvars.foo;
}
public static doThing():bar {
return YourSingleton.foo
}
}
Sie können die Klasse importieren und YourSingleton.doThing()
in jeder anderen Klasse darauf verweisen . Aber denken Sie daran, da es sich um eine statische Klasse handelt, die keinen Konstruktor hat. Daher verwende ich normalerweise eine intialise()
Methode, die von einer Klasse aufgerufen wird, die den Singleton importiert:
import {YourSingleton} from 'singleton.ts';
YourSingleton.initialise(params);
let _result:bar = YourSingleton.doThing();
Vergessen Sie nicht, dass in einer statischen Klasse jede Methode und Variable auch statisch sein muss, damit Sie stattdessen this
den vollständigen Klassennamen verwenden YourSingleton
.
Hier ist noch eine andere Möglichkeit, dies mit einem konventionelleren Javascript-Ansatz unter Verwendung eines IFFE zu tun :
module App.Counter {
export var Instance = (() => {
var i = 0;
return {
increment: (): void => {
i++;
},
getCount: (): number => {
return i;
}
}
})();
}
module App {
export function countStuff() {
App.Counter.Instance.increment();
App.Counter.Instance.increment();
alert(App.Counter.Instance.getCount());
}
}
App.countStuff();
Sehen Sie sich eine Demo an
Instance
Variable hinzuzufügen ? Sie können einfach die Variable und die Funktionen direkt darunter platzieren App.Counter
.
Eine andere Möglichkeit ist die Verwendung von Symbolen in Ihrem Modul. Auf diese Weise können Sie Ihre Klasse schützen, auch wenn der Endbenutzer Ihrer API normales Javascript verwendet:
let _instance = Symbol();
export default class Singleton {
constructor(singletonToken) {
if (singletonToken !== _instance) {
throw new Error("Cannot instantiate directly.");
}
//Init your class
}
static get instance() {
return this[_instance] || (this[_instance] = new Singleton(_singleton))
}
public myMethod():string {
return "foo";
}
}
Verwendung:
var str:string = Singleton.instance.myFoo();
Wenn der Benutzer Ihre kompilierte API-JS-Datei verwendet, wird auch eine Fehlermeldung angezeigt, wenn er versucht, Ihre Klasse manuell zu instanziieren:
// PLAIN JAVASCRIPT:
var instance = new Singleton(); //Error the argument singletonToken !== _instance symbol
Dies ist der einfachste Weg
class YourSingletoneClass {
private static instance: YourSingletoneClass;
private constructor(public ifYouHaveAnyParams: string) {
}
static getInstance() {
if(!YourSingletoneClass.instance) {
YourSingletoneClass.instance = new YourSingletoneClass('If you have any params');
}
return YourSingletoneClass.instance;
}
}
namespace MySingleton {
interface IMySingleton {
doSomething(): void;
}
class MySingleton implements IMySingleton {
private usePrivate() { }
doSomething() {
this.usePrivate();
}
}
export var Instance: IMySingleton = new MySingleton();
}
Auf diese Weise können wir eine Schnittstelle anwenden, anders als in Ryan Cavanaughs akzeptierter Antwort.
Nachdem ich diesen Thread durchsucht und mit allen oben genannten Optionen herumgespielt hatte, entschied ich mich für einen Singleton, der mit geeigneten Konstruktoren erstellt werden kann:
export default class Singleton {
private static _instance: Singleton
public static get instance(): Singleton {
return Singleton._instance
}
constructor(...args: string[]) {
// Initial setup
Singleton._instance = this
}
work() { /* example */ }
}
Es würde eine Ersteinrichtung erfordern (in main.ts
, oderindex.ts
) , die leicht von implementiert werden kann
new Singleton(/* PARAMS */)
Rufen Sie dann irgendwo in Ihrem Code einfach an Singleton.instnace
. In diesem Fall work
würde ich anrufen , um fertig zu werdenSingleton.instance.work()