JavaScript和TypeScript的单例写法(面向对象)

原创 2017年06月11日 21:56:29

一、单例类的作用

在我们平时开发H5游戏过程中,有某别对象,希望在内存中只有一份实例,其他任何地方想要获取到这个实例,只能通过这个类提供的静态方法来获取到实例,而任何地方进行new来进行构造的话,都会报错。总结一下这个单例类的要求

  1. 这个类只允许进行一个new的调用构造函数行为
  2. 提供静态方法来访问唯一实例
  3. 提供重复new会报错的设计
  4. 具备合理优化的设计模式

二、单例设计模式

后面是借鉴的AS3的经典写法,写出的JavaScript和TypeScript版本的单例类。
这里的Singleton其实是基类的来的,子类只要继承它就自然能够获得唯一实例的判断了。相当的方便。
实际生产环境的单例基类的内部类封装:

/** 存放初始化过的构造函数 **/
private static classMap: Dictionary<any, Object> = new Dictionary();
constructor()
{
    var clazz: any = clazz = ClassUtils.forInstance(this);
    //为空时,表示浏览器不支持这样读取构造函数
    if (!clazz)
        return;
    // 防止重复实例化
    if (Singleton.classMap.hasKey(clazz))
        throw new Error(this + " 只允许实例化一次!");
    else
        Singleton.classMap.put(clazz, this);
}

可以看到把key和value用一个Dictionary对象来封装了。不过在这里就不搞太复杂了。直接给出最完整的代码。

设计原理是通过一个所有实例都共享的一个数组来存放已经初始化过的对象,然后在那个对象的构造函数里检测这个对象是否已经实例话过了。所以在这里我们就采用2个数组来存放。

/** 存放初始化过的构造函数,这里用数组来存放构造函数 **/
private static classKeys:Function[] = [];
private static classValues:any[] = [];

这里设计得比较面向对象,比较适合开发Html5游戏了。

一、TypeScript的单例写法

Singleton.ts

/**
 * 所有单例的基类,做了单例的基础检查。所有子类最好都写一个getInstance的静态方法来获取
 * @author sodaChen
 * Date:2012-10-29
 */
class Singleton
{
    //其实实际的开发项目中,不一定会用到数组,有可能会把数组之类的进行封装
    /** 存放初始化过的构造函数,这里用数组来存放构造函数 **/
    private static classKeys:Function[] = [];
    private static classValues:any[] = [];

    constructor()
    {
        var clazz: any = this["constructor"];
        //为空时,表示浏览器不支持这样读取构造函数
        if (!clazz)
            return;
        // 防止重复实例化
        if (Singleton.classKeys.indexOf(clazz) != -1)
            throw new Error(this + " 只允许实例化一次!");
        else
        {
            Singleton.classKeys.push(clazz);
            Singleton.classValues.push(this);
        }

    }
    //注意,Singleton是要替换成你自己实现的子类 这里没有实际的作用
    private static instance:Singleton;
    /**
     * 获取实例的静态方法实例
     * @return
     *
     */
    public static getInstance():Singleton
    {
        if(!this.instance)
        {
            this.instance = new Singleton();
        }
        return this.instance;
    }
    /**
     * 销毁方法。事实上单例是很少进行销毁的
     */
    destroy(o: any = null): void
    {
        this.onDestroy();
        Singleton.removeInstance(this["constructor"]);
    }

    /**
     * 子类重写的方法
     */
    protected onDestroy(): void
    {

    }
    /**
     * 删除单例的实例(不对单例本身做任何的销毁,只是删除他的引用)
     * @param clazz 单例的Class对象
     *
     */
    static removeInstance(clazz: Function): void
    {
        var index: number = this.classKeys.indexOf(clazz);
        if (index == -1)
        {
            return null;
        }
        this.classKeys.splice(index, 1);
        this.classValues.splice(index, 1);
    }

    /**
     * 是否存放有这个构造函数
     * @param clazz 构造函数
     * @return {boolean}
     */
    static getFunValue(clazz: Function):any
    {
        let funs:Function[] = this.classKeys;
        let length:number = funs.length;
        for(let i:number = 0; i < length; i++)
        {
            if(clazz == funs[i])
                return this.classValues[i];
        }
        return null;
    }

    /**
     * 获取单例类,若不存在则创建.所有的单例创建的时候,都必须使用这个方法来创建,这样可以做到统一管理单例
     * @param clazz 任意需要实现单例效果的类
     * @return
     *
     */
    static getInstanceOrCreate(clazz:any): any
    {
        var obj: any = this.getFunValue(clazz);
        if (obj)
        {
            return obj;
        }
        obj = new clazz();
        //不是Singleton的子类,则手动添加Singleton构造器会自动添加到classMap
        if (!(obj instanceof Singleton))
        {
            this.classKeys.push(clazz);
            this.classValues.push(obj);
        }
        return obj;
    }
}

测试代码:

var singleton1 = Singleton.getInstance();
console.log("通过Singleton.getInstance实例:" + singleton1);
var singleton2 = Singleton.getInstance();
console.log("singleton1 == singleton2:" + (singleton1 == singleton2));
new Singleton();

测试输出结果:

通过Singleton.getInstance实例:[object Object]
singleton1 == singleton2:true
Uncaught Error: [object Object] 只允许实例化一次!
  Singleton 
  main  
  onload

实际使用中,可以直接继承Singleton,再增加一个静态方法就可以了,比如TickMgr.ts

private static instance:TickMgr;
/**
 * 获取实例的静态方法实例
 * @return
 *
 */
public static getInstance():TickMgr
{
    if(!this.instance)
    {
        this.instance = new TickMgr();
    }
    return this.instance;
}

二、JavaScript的单例写法

原理是一样的,因为TypeScript本身就是编译成js代码的。

/**
 * 所有单例的基类,做了单例的基础检查。所有子类最好都写一个getInstance的静态方法来获取
 * @author sodaChen
 * Date:2012-10-29
 */
var Singleton = (function () {
    function Singleton() {
        var clazz = this["constructor"];
        //为空时,表示浏览器不支持这样读取构造函数
        if (!clazz)
            return;
        // 防止重复实例化
        if (Singleton.classKeys.indexOf(clazz) != -1)
            throw new Error(this + " 只允许实例化一次!");
        else {
            Singleton.classKeys.push(clazz);
            Singleton.classValues.push(this);
        }
    }
    /**
     * 获取实例的静态方法实例
     * @return
     *
     */
    Singleton.getInstance = function () {
        if (!this.instance) {
            this.instance = new Singleton();
        }
        return this.instance;
    };
    /**
     * 销毁方法。事实上单例是很少进行销毁的
     */
    Singleton.prototype.destroy = function (o) {
        if (o === void 0) { o = null; }
        this.onDestroy();
        Singleton.removeInstance(this["constructor"]);
    };
    /**
     * 子类重写的方法
     */
    Singleton.prototype.onDestroy = function () {
    };
    /**
     * 删除单例的实例(不对单例本身做任何的销毁,只是删除他的引用)
     * @param clazz 单例的Class对象
     *
     */
    Singleton.removeInstance = function (clazz) {
        var index = this.classKeys.indexOf(clazz);
        if (index == -1) {
            return null;
        }
        this.classKeys.splice(index, 1);
        this.classValues.splice(index, 1);
    };
    /**
     * 是否存放有这个构造函数
     * @param clazz 构造函数
     * @return {boolean}
     */
    Singleton.getFunValue = function (clazz) {
        var funs = this.classKeys;
        var length = funs.length;
        for (var i = 0; i < length; i++) {
            if (clazz == funs[i])
                return this.classValues[i];
        }
        return null;
    };
    /**
     * 获取单例类,若不存在则创建.所有的单例创建的时候,都必须使用这个方法来创建,这样可以做到统一管理单例
     * @param clazz 任意需要实现单例效果的类
     * @return
     *
     */
    Singleton.getInstanceOrCreate = function (clazz) {
        var obj = this.getFunValue(clazz);
        if (obj) {
            return obj;
        }
        obj = new clazz();
        //不是Singleton的子类,则手动添加Singleton构造器会自动添加到classMap
        if (!(obj instanceof Singleton)) {
            this.classKeys.push(clazz);
            this.classValues.push(obj);
        }
        return obj;
    };
    return Singleton;
}());
//其实实际的开发项目中,不一定会用到数组,有可能会把数组之类的进行封装
/** 存放初始化过的构造函数,这里用数组来存放构造函数 **/
Singleton.classKeys = [];
Singleton.classValues = [];

测试代码是一样的。

三、JavaScript更简单和比较传统的单例写法

这个写法其实就是比较偏向做js前端,而不是h5游戏的。
一般是创建一个命名空间来存放,其他地方使用就不会有new的写法了
比如

var asf = {};  

asf.singleton = {} ;  
asf.TickMgr = {};  

外面则是直接调用

asf.singleton.pay("mc");
asf.TickMgr.tick();
版权声明:本文为博主原创文章,转载必须声明出处和作者。地址:http://blog.csdn.net/sujun10 作者:弃天笑

TypeScript设计模式之单例、建造者、原型

看看用TypeScript怎样实现常见的设计模式,顺便复习一下。 单例模式 Singleton 特点:在程序的生命周期内只有一个全局的实例,并且不能再new出新的实例。 用处:在一些只需要一个对...
  • jianglibo1024
  • jianglibo1024
  • 2017年03月08日 14:27
  • 201

设计模式-单例模式

单例模式的应用场景有些时候我们程序只需要一个对象:线程池,缓存,打印机这种硬件设备 比如打印机,如果是多个对象,又对应一个硬件,各自设置打印内容,就会混淆。单例模式 : 确保一个类只有一个实例,并提...
  • libra_ts
  • libra_ts
  • 2017年02月05日 22:55
  • 416

Typescript 入门 及 使用Visual Studio Code搭建TypeScript开发环境

Typescript官网,汉语 https://www.tslang.cn/samples/index.html Typescript快速入门 https://www.w3cschool.cn/...
  • liangxw1
  • liangxw1
  • 2017年08月08日 06:13
  • 388

【第7篇】TypeScript泛型的案例代码详解

TypeScript泛型的案例代码详解
  • jilongliang
  • jilongliang
  • 2015年08月08日 09:37
  • 4998

Typescript代码实例解析

简介:TypeScript 是一种由微软开发的自由和开源的编程语言,它是JavaScript的一个超集,扩展了JavaScript的语法。语法特性 类 Classes 接口 Interfaces ...
  • franktaoge
  • franktaoge
  • 2017年09月21日 12:03
  • 741

java中最严谨的单例模式的实现方式

现在很多有关java的书籍、视频、博客等有讲解java中的单例模式的实现,但是遗憾的是,很多讲的都不是完全正确不是完全标准的。 java一个正确无误的单例模式应该包含以下几个方面: 1.有一个priv...
  • xiaoyang0611
  • xiaoyang0611
  • 2012年09月24日 15:14
  • 443

Egret开发《消灭方块》后记(二)对象池的应用

对于性能的追求是永无止境的,此文只做抛砖引玉,欢迎讨论。 用好对象池绝对能提高很多项目的执行效率,下面结合我在《消灭方块》的开发介绍一下对象池的应用。...
  • eperfect
  • eperfect
  • 2014年09月07日 20:40
  • 2355

深入理解正则表达式的环视

从Zjmainstay的深入理解正则表达式高级教程中截取的一部分内容: 环视(断言/零宽断言) 环视,在不同的地方又称之为零宽断言,简称断言。  用一句通俗的话解释:  环视,就是先从全局...
  • piglite
  • piglite
  • 2018年01月16日 11:03
  • 37

ECMAScript垃圾回收机制及性能提升

ECMAScript的垃圾回收主要有两种算法,一种是引用计数,一种是标记清除; 引用计数 引用计数的主要思想是为内存中的每个对象保持一个计数器,如果一个对象的引用次数为0,那么这样的对象就可以...
  • y2010081134
  • y2010081134
  • 2017年05月01日 23:30
  • 172

TypeScript:更好的JavaScript

作为编程语言的TypeScript 关于TypeScript,首先要认识的一点就是:它是Anders Hejlsberg的作品。Anders是第一流的编程语言设计师,也是第一流的编译器实现者。作为Ob...
  • xiaozhi_2016
  • xiaozhi_2016
  • 2017年03月21日 17:11
  • 1147
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:JavaScript和TypeScript的单例写法(面向对象)
举报原因:
原因补充:

(最多只允许输入30个字)