分享一个TS的单例基类

因为 TS 的语法,泛型类的 T 不能用于 static 方法,所以用泛型函数去实现。

又不想因为调用 getInstance 在用 <> 传个类型,所以在子类封装一个 get instance。

这样代码跳转也是直接跳到对应的子类而不是父类

export class Singleton {
    protected constructor() {}
    private static _instance: any;
    protected static getInstance<T extends Singleton>(): T {
        if (!this._instance) {
            this._instance = new this();
        }
        return this._instance;
    }
    public static deleteInstance() {
        if (this._instance) {
            delete this._instance;
        }
    }
}

export class UserMgr extends Singleton {
    public static get instance() {
        return super.getInstance<UserMgr>();
    }
}

3赞

我的评价是 不如export new和代码片段

1赞

export new 不能删除单例对象重新 new 个新的

单例为什么还要new个新的

导出式单例就够用了 非要说的话 继承单例基类后也无法再继承别的了 你也可以看看装饰器单例

子游戏数据管理,进游戏用,出游戏释放

多继承再TS中你要一层一层继承下来,最后在继承单例基类。装饰器怎么说呢,不完全符合单例的规则

建议内部实现一个reset接口

统一使用C++/MVC格式,方便移植

1、装饰器只是在类的原型上加了个 getInstance 方法,constructor 还是 public,不符合单例的规则。

2、用 export new 这种方法,进游戏就把所有 export new 的类全部实例出来了,批次的逻辑开销大。单例是在调用 getInstance 时没有才实例。

3、说下为什么有些单例要 delete,比如一个游戏有五六十个子游戏,每个子游戏有1~3个单例,这些单例只有在自身的子游戏中才用得到,写个reset函数只是把数据初始化,内存的开销还是在的,从逻辑严谨性来说玩家离开这个子游戏,其中的数据或者资源应该释放掉,如果要做游戏资源或者数据缓存是另一回事了。

挺好的,可以结合vscode代码片段,根据文件名自动填入类名。

"单例": {
		"prefix": "inst",
		"body": [
			"public static get instance() { return super.getInstance<$TM_FILENAME_BASE>(); }",
			"$1"
		],
	},

请问 单例的特点是什么?

export default class Singleton{

static Instance<T extends {}>(this: new () => T): T {

    if(!(<any>this).instance){

        (<any>this).instance = new this();

    }

    return (<any>this).instance;

}

}

这个就够用了

请问这个代码可以做到这样子吗?
//xxMgr.ts
class xxMgr extend Singleton{
funcA(){
console.log(‘funA is here’);
}
}
//other.ts
import xxMgr from ‘xxMgr’;
xxMgr.funcA();
–我个人很讨厌那种需要xxMgr后面还要写个instance才能点出来方法的写法。
–xxMgr.instance.funcA();//讨厌这个

我的单例基类

class SingletonBase {
    private static _ins: any;
    static getInstance<T>(this: new () => T): T {
        if (!(this as any)._ins) {
            (this as any)._ins = new this();
        }
        // eslint-disable-next-line @typescript-eslint/no-unsafe-return
        return (this as any)._ins;
    }
}

Object.defineProperty(SingletonBase, 'instance', {
    get () {
        // eslint-disable-next-line @typescript-eslint/no-unsafe-return
        return (this).getInstance();
    },
});

interface SingletonIns<T> {
    get instance(): T;
};

/**
 * 单例基类
 * usage:
 * `class Test extends Singleton<Test>() {}`
 */
export default function XSingleton<T> () {
    return SingletonBase as (typeof SingletonBase & SingletonIns<T>);
}

加个这个就行了 export const xx = xx.instance;

还是请教一下,我的写法在vscode里面没报错,但是实际跑起来没找到对象啊。
—xxMgr.ts
export class Singleton {

protected constructor() { }

static _instance: any;

protected static getInstance<T extends Singleton>(): T {

    if (!this._instance) {

        this._instance = new this();

    }

    return this._instance;

}

public static deleteInstance() {

    if (this._instance) {

        delete this._instance;

    }

}

}

class XXMgr extends Singleton {

funcA() {

    console.error('funA is here');

}

}
export const xxMgr = XXMgr._instance;

–main.ts
import { xxMgr } from ‘…/mgr/xxMgr’;
start(){
xxMgr.funA();—报错:TypeError: Cannot read properties of undefined (reading ‘funA’)
}
请问应该怎么修改啊?

把这里的protected改成public,如何用的时候先获取单例:
export const xxMgr = XXMgr.getInstance()
补充一点_instance居然前面有下划线_,那它应该是私有的:private static _instance:any

谢谢,但是还是不对劲。
–xxMgr.ts
export class Singleton {
protected constructor() { }
private static _instance: any;
public static getInstance(): T {
if (!this._instance) {
this._instance = new this();
}
return this._instance;
}
public static deleteInstance() {
if (this._instance) {
delete this._instance;
}
}
}

class XXMgr extends Singleton {
    public static funcA() {
        console.error('funA is here');
    }
}
let xxMgr: XXMgr;

export default xxMgr = XXMgr.getInstance();//这里的改动不影响结果

—main.ts:
import xxMgr from ‘…/Manager/xxMgr’;
start() {
xxMgr.funA();//vscode里面报错:类型“XXMgr”上不存在属性“funA”
}