Answers:
TypeScript中的Singleton类通常是反模式。您可以简单地使用名称空间来代替。
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()
呢?
从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;
感谢@Drenai指出,如果您使用原始的编译javascript编写代码,则将无法防止多重实例化,因为TS的约束消失了,构造函数也不会被隐藏。
我发现的最好方法是:
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;
}
}
使用方法如下:
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/
下面的方法创建一个Singleton类,可以像常规类一样使用它:
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;
}
每个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;
}
用法: var obj = Singleton.getInstance();
new Class(...)
语法。
我很惊讶没有在这里看到以下模式,它实际上看起来非常简单。
// shout.ts
class ShoutSingleton {
helloWorld() { return 'hi'; }
}
export let Shout = new ShoutSingleton();
用法
import { Shout } from './shout';
Shout.helloWorld();
Shout
类
您可以为此使用类表达式(我相信从1.6开始)。
var x = new (class {
/* ... lots of singleton logic ... */
public someMethod() { ... }
})();
或使用名称(如果您的班级需要在内部访问其类型)
var x = new (class Singleton {
/* ... lots of singleton logic ... */
public someMethod(): Singleton { ... }
})();
另一种选择是使用一些静态成员在单例内部使用本地类
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();
将以下6行添加到任何类中,使其成为“ Singleton”。
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
[编辑]:如果您希望通过属性而不是方法来获取实例,请使用Alex答案。
new MySingleton()
说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;
}
如何使用
第1步
class MapManager extends Singleton<MapManager>{
//do something
public init():void{ //do }
}
第2步
MapManager.Instance(MapManager).init();
您还可以使用Object.Freeze()函数。其简单易行:
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)
在构造函数中麻烦呢?如果您在导出之前创建了多个实例,这是否只是一种额外的预防措施?
我发现TypeScript编译器完全可以使用它的新版本,并且我认为更好,因为它不需要getInstance()
不断地调用方法。
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;
}
}
这确实具有不同的缺点。如果您Singleton
确实具有任何属性,那么除非您使用值初始化它们,否则Typescript编译器将抛出合适的值。这就是为什么我_express
在示例类中包括一个属性的原因,因为除非用一个值初始化它,即使您稍后在构造函数中分配它,Typescript也会认为它尚未定义。可以通过禁用严格模式来解决此问题,但我不希望这样做。我应该指出,此方法还有另一个缺点,因为构造器实际上每次被调用时,都会在技术上创建另一个实例,但无法访问它。从理论上讲,这可能会导致内存泄漏。
这可能是在打字稿中制作单例的最长过程,但是在较大的应用程序中,对我来说效果更好。
首先,您需要在“ ./utils/Singleton.ts”中添加一个Singleton类:
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; }
}
}
现在,假设您需要一个路由器单例“ ./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();
}
太好了!现在可以在需要的地方初始化或导入:
/// <reference path="./navigation/Router.ts" />
import router = navigation.Router;
router.init();
router.init(); // Throws error!.
以这种方式进行单例的好处是,您仍然可以使用打字稿类的所有功能,它可以为您提供很好的智能感知,单例逻辑保持某种程度的分离,并且在需要时很容易删除。
我的解决方案:
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
。这样,如果您new
使用该类,则将获得现有对象,而不是新对象。
在Typescript中,不一定必须遵循new instance()
Singleton方法。导入的无需构造函数的静态类也可以同样工作。
考虑:
export class YourSingleton {
public static foo:bar;
public static initialise(_initVars:any):void {
YourSingleton.foo = _initvars.foo;
}
public static doThing():bar {
return YourSingleton.foo
}
}
您可以导入该类并YourSingleton.doThing()
在其他任何类中进行引用。但是请记住,因为这是一个静态类,所以它没有构造函数,所以我通常使用intialise()
从导入Singleton的类调用的方法:
import {YourSingleton} from 'singleton.ts';
YourSingleton.initialise(params);
let _result:bar = YourSingleton.doThing();
不要忘记,在静态类中,每个方法和变量也都必须是静态的,因此this
您可以使用完整的类名来代替YourSingleton
。
这是使用IFFE使用更常规的javascript方法的另一种方法:
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();
观看演示
Instance
变量的原因是什么?您只需将变量和函数直接放在App.Counter
。
另一种选择是在模块中使用符号。这样,即使您的API的最终用户使用的是普通Javascript,也可以保护您的类:
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";
}
}
用法:
var str:string = Singleton.instance.myFoo();
如果用户使用的是已编译的API js文件,则尝试手动实例化您的类时也会收到错误消息:
// PLAIN JAVASCRIPT:
var instance = new Singleton(); //Error the argument singletonToken !== _instance symbol
这是最简单的方法
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;
}
}
在检查完该线程并尝试了上述所有选项之后,我选择了可以通过适当的构造函数创建的Singleton:
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 */ }
}
它将需要进行初始设置(以main.ts
或index.ts
),可以通过以下方式轻松实现
new Singleton(/* PARAMS */)
然后,在代码中的任何地方,只需调用Singleton.instnace
;在这种情况下,为了work
完成,我会打电话给Singleton.instance.work()