深入理解JavaScript系列(25):设计模式之单例模式详解


Posted in Javascript onMarch 03, 2015

介绍

从本章开始,我们会逐步介绍在JavaScript里使用的各种设计模式实现,在这里我不会过多地介绍模式本身的理论,而只会关注实现。OK,正式开始。

在传统开发工程师眼里,单例就是保证一个类只有一个实例,实现的方法一般是先判断实例存在与否,如果存在直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。在JavaScript里,单例作为一个命名空间提供者,从全局命名空间里提供一个唯一的访问点来访问该对象。

正文

在JavaScript里,实现单例的方式有很多种,其中最简单的一个方式是使用对象字面量的方法,其字面量里可以包含大量的属性和方法:

var mySingleton = {

    property1: "something",

    property2: "something else",

    method1: function () {

        console.log('hello world');

    }

};

如果以后要扩展该对象,你可以添加自己的私有成员和方法,然后使用闭包在其内部封装这些变量和函数声明。只暴露你想暴露的public成员和方法,样例代码如下:
var mySingleton = function () {
    /* 这里声明私有变量和方法 */

    var privateVariable = 'something private';

    function showPrivate() {

        console.log(privateVariable);

    }
    /* 公有变量和方法(可以访问私有变量和方法) */

    return {

        publicMethod: function () {

            showPrivate();

        },

        publicVar: 'the public can see this!'

    };

};
var single = mySingleton();

single.publicMethod();  // 输出 'something private'

console.log(single.publicVar); // 输出 'the public can see this!'

上面的代码很不错了,但如果我们想做到只有在使用的时候才初始化,那该如何做呢?为了节约资源的目的,我们可以另外一个构造函数里来初始化这些代码,如下:

var Singleton = (function () {

    var instantiated;

    function init() {

        /*这里定义单例代码*/

        return {

            publicMethod: function () {

                console.log('hello world');

            },

            publicProperty: 'test'

        };

    }
    return {

        getInstance: function () {

            if (!instantiated) {

                instantiated = init();

            }

            return instantiated;

        }

    };

})();
/*调用公有的方法来获取实例:*/

Singleton.getInstance().publicMethod();

知道了单例如何实现了,但单例用在什么样的场景比较好呢?其实单例一般是用在系统间各种模式的通信协调上,下面的代码是一个单例的最佳实践:

var SingletonTester = (function () {
    //参数:传递给单例的一个参数集合

    function Singleton(args) {
        //设置args变量为接收的参数或者为空(如果没有提供的话)

        var args = args || {};

        //设置name参数

        this.name = 'SingletonTester';

        //设置pointX的值

        this.pointX = args.pointX || 6; //从接收的参数里获取,或者设置为默认值

        //设置pointY的值

        this.pointY = args.pointY || 10;
    }
    //实例容器

    var instance;
    var _static = {

        name: 'SingletonTester',
        //获取实例的方法

        //返回Singleton的实例

        getInstance: function (args) {

            if (instance === undefined) {

                instance = new Singleton(args);

            }

            return instance;

        }

    };

    return _static;

})();
var singletonTest = SingletonTester.getInstance({ pointX: 5 });

console.log(singletonTest.pointX); // 输出 5

其它实现方式

方法1:

function Universe() {
    // 判断是否存在实例

    if (typeof Universe.instance === 'object') {

        return Universe.instance;

    }
    // 其它内容

    this.start_time = 0;

    this.bang = "Big";
    // 缓存

    Universe.instance = this;
    // 隐式返回this

}
// 测试

var uni = new Universe();

var uni2 = new Universe();

console.log(uni === uni2); // true

方法2:

function Universe() {
    // 缓存的实例

    var instance = this;
    // 其它内容

    this.start_time = 0;

    this.bang = "Big";
    // 重写构造函数

    Universe = function () {

        return instance;

    };

}
// 测试

var uni = new Universe();

var uni2 = new Universe();

uni.bang = "123";

console.log(uni === uni2); // true

console.log(uni2.bang); // 123

方法3:

function Universe() {
    // 缓存实例

    var instance;
    // 重新构造函数

    Universe = function Universe() {

        return instance;

    };
    // 后期处理原型属性

    Universe.prototype = this;
    // 实例

    instance = new Universe();
    // 重设构造函数指针

    instance.constructor = Universe;
    // 其它功能

    instance.start_time = 0;

    instance.bang = "Big";
    return instance;

}


// 测试

var uni = new Universe();

var uni2 = new Universe();

console.log(uni === uni2); // true
// 添加原型属性

Universe.prototype.nothing = true;
var uni = new Universe();
Universe.prototype.everything = true;
var uni2 = new Universe();
console.log(uni.nothing); // true

console.log(uni2.nothing); // true

console.log(uni.everything); // true

console.log(uni2.everything); // true

console.log(uni.constructor === Universe); // true

方式4:

var Universe;
(function () {
    var instance;
    Universe = function Universe() {
        if (instance) {

            return instance;

        }
        instance = this;
        // 其它内容

        this.start_time = 0;

        this.bang = "Big";

    };

} ());
//测试代码

var a = new Universe();

var b = new Universe();

alert(a === b); // true

a.bang = "123";

alert(b.bang); // 123
Javascript 相关文章推荐
javascript基于DOM实现省市级联下拉框的方法
May 14 Javascript
js实现的简洁网页滑动tab菜单效果代码
Aug 24 Javascript
jQuery增加与删除table列的方法
Mar 01 Javascript
高效Web开发的10个jQuery代码片段
Jul 22 Javascript
JS实现十字坐标跟随鼠标效果
Dec 25 Javascript
教你如何用node连接redis的示例代码
Jul 12 Javascript
Vue press 支持图片放大功能的实例代码
Nov 09 Javascript
jQuery实现的点击显示隐藏下拉菜单功能完整示例
May 17 jQuery
详解node登录接口之密码错误限制次数(含代码)
Oct 25 Javascript
layui写后台表格思路和赋值用法详解
Nov 14 Javascript
js判断非127开头的IP地址的实例代码
Jan 05 Javascript
React如何创建组件
Jun 27 Javascript
js+jquery常用知识点汇总
Mar 03 #Javascript
js实现宇宙星空背景效果的方法
Mar 03 #Javascript
Angular中的Promise对象($q介绍)
Mar 03 #Javascript
Javascript设计模式之观察者模式的多个实现版本实例
Mar 03 #Javascript
Node.js 学习笔记之简介、安装及配置
Mar 03 #Javascript
JS+CSS模拟可以无刷新显示内容的留言板实例
Mar 03 #Javascript
JavaScript跨浏览器获取页面中相同class节点的方法
Mar 03 #Javascript
You might like
默默简单的写了一个模板引擎
2007/01/02 PHP
如何使用Linux的Crontab定时执行PHP脚本的方法
2011/12/19 PHP
php基于curl实现的股票信息查询类实例
2016/11/11 PHP
在Laravel5.6中使用Swoole的协程数据库查询
2018/06/15 PHP
PHP+Apache实现二级域名之间共享cookie的方法
2019/07/24 PHP
Javascript 个人笔记(没有整理,很乱)
2007/07/07 Javascript
js正确获取元素样式详解
2009/08/07 Javascript
js解析xml字符串和xml文档实现原理及代码(针对ie与火狐)
2013/02/02 Javascript
深入理解JavaScript 闭包究竟是什么
2013/04/12 Javascript
document.forms用法示例介绍
2014/06/26 Javascript
Javascript将双字节字符转换成单字节字符并计算长度
2016/06/22 Javascript
简单谈谈ES6的六个小特性
2016/11/18 Javascript
layui.js实现的表单验证功能示例
2017/11/15 Javascript
vue.js,ajax渲染页面的实例
2018/02/11 Javascript
详解node.js 下载图片的 2 种方式
2018/03/02 Javascript
jQuery.extend 与 jQuery.fn.extend的用法及区别实例分析
2018/07/25 jQuery
全面了解JavaScript的作用域链
2019/04/03 Javascript
基于Bootstrap和JQuery实现动态打开和关闭tab页的实例代码
2019/06/10 jQuery
typescript配置alias的详细步骤
2020/08/12 Javascript
Vue this.$router.push(参数)实现页面跳转操作
2020/09/09 Javascript
详解ES6 中的Object.assign()的用法实例代码
2021/01/11 Javascript
[01:03:00]DOTA2上海特级锦标赛A组败者赛 EHOME VS CDEC第一局
2016/02/25 DOTA
python实现的二叉树算法和kmp算法实例
2014/04/25 Python
Python解析json之ValueError: Expecting property name enclosed in double quotes: line 1 column 2(char 1)
2017/07/06 Python
Python中序列的修改、散列与切片详解
2017/08/27 Python
Python2.7环境Flask框架安装简明教程【已测试】
2018/07/13 Python
Python 实现子类获取父类的类成员方法
2019/01/11 Python
python web框架 django wsgi原理解析
2019/08/20 Python
keras和tensorflow使用fit_generator 批次训练操作
2020/07/03 Python
半年思想汇报
2013/12/30 职场文书
党员政治学习材料
2014/05/14 职场文书
社区科普工作方案
2014/06/03 职场文书
初中开学典礼新闻稿
2015/07/17 职场文书
2015年征兵工作总结
2015/07/23 职场文书
大学生,三分钟即兴演讲稿
2019/07/22 职场文书
pytorch 梯度NAN异常值的解决方案
2021/06/05 Python