JavaScript设计模式与开发实践-读书笔记(4)单例模式

摘要:
这违反了“单一责任原则”。这样,CreateDiv就成为一个公共类。它可以与proxySingletonCreateDiv相结合,以实现单例模式的效果。但是JavaScript是一种无类语言,这就是为什么复制单例模式的概念是没有意义的。单例模式的核心是确保只有一个实例并提供全局访问。全局模式不是单例模式,但在JavaScript开发中,我们经常使用全局变量作为单例模式。以WebQQ的登录浮动窗口为例,结合全局变量介绍了一个惯性的例子。

单例模式的定义是:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

1.实现单例模式

要实现一个标准的单例模式并不复杂,无非是用一个变量来标志当前是否已经为某个类创建过对象,如果是,则在下一次获取该类的实例时,直接返回之前创建的对象。

    var Singleton = function(name){
        this.name = name;
        this.instance = null;
    };
    Singleton.prototype.getName = function(){
        alert(this.name);
    };
    Singleton.getInstance = function(name){
        if(!this.instance){
            this.instance = new Singleton(name);
        }
        return this.instance;
    };
    var a = Singleton.getInstance('sven1');
    var b = Singleton.getInstance('sven2');
    alert(a === b);  //true

或者:

    var Singleton = function(name){
        this.name = name;
    };
    Singleton.prototype.getName = function(){
        alert(this.name);
    };
    Singleton.getInstance = (function(){
        var instance = null;
        return function(name){
            if(!instance){
                instance = new Singleton(name);
            }
            return instance;
        }
    })();
    var a = Singleton.getInstance('sven1');
    var b = Singleton.getInstance('sven2');
    alert(a === b);  //true

我们通过Singleton.getInstance来获取Singleton类的唯一对象,这种方式相对简单,但是有一个问题,增加了这个类的"不透明性"。
这段单例模式代码的意义并不大。

2.透明的单例模式

我们现在的目标是实现一个"透明"的单例类,用户从这个类中创建对象的时候,可以像使用其他任何普通类一样。

var CreateDiv = (function(){
        var instance;
        var CreateDiv = function(html){
            if(instance){
                return instance;
            }
            this.html = html;
            this.init();
            return instance = this;
        };

        CreateDiv.prototype.init = function(){
            var div = document.createElement('div');
            div.innerHTML = this.html;
            document.body.appendChild(div);
        };

        return CreateDiv;

    })();

    var a = new CreateDiv('sven1');
    var b = new CreateDiv('sven2');
    alert(a === b);  

但它同样有一些缺点。
CreateDiv的构造函数实际上负责了两件事情。第一是创建对象和执行初始化方法init方法,第二是保证只有一个对象。违背了"单一职责原则"。

3.用代理实现单例模式

    var CreateDiv = function(html){        
        this.html = html;
        this.init();
    };

    CreateDiv.prototype.init = function(){
        var div = document.createElement('div');
        div.innerHTML = this.html;
        document.body.appendChild(div);
    };
    //引入代理类
    var ProxySingletonCreateDiv = (function(){
        var instance;
        return function(html){
            if(!instance){
                instance = new CreateDiv(html);
            }
            return instance;
        }
    })();

    var a = new ProxySingletonCreateDiv('sven1');
    var b = new ProxySingletonCreateDiv('sven2');
    alert(a === b);

通过引用代理类的方式,我们同样完成了一个单例模式的编写,跟之前不同的是,现在我们把负责管理单例模式的逻辑移到了代理类ProxySingletonCreateDiv中。这样一来,CreateDiv就变成了一个普通的类,它跟proxySingletonCreateDiv组合起来可以达到单例模式的效果。

4.JavaScript中的单例模式

单例对象从"类"中创建而来。在以类为中心的语言中,这是很自然的做法。但JavaScript是一门无类(class-free)语言,也正因为如此,生搬单例模式的概念并无意义。

单例模式的核心是确保只有一个实例,并提供全局访问。

全局模式不是单例模式,但在JavaScript开发中,我们经常会把全局变量当成单例模式来使用。

var a = {};

这种方式创建对象a时,对象a确实是独一无二的。如果a变量被声明在全局作用域下,则我们可以在代码中任何位置使用这个变量,全局变量提供给全局访问是理所当然的。这样就满足了单例模式的两个条件。

但是全局变量很容易造成命名空间污染。还有变量冲突。

作为普通的开发者,我们有必要尽量减少全局变量的使用,即使需要,也要把它的污染降到最低。

以下几种方式可以相对降低全局变量带来的命名污染。

1.使用命名空间

最简单的方法是用对象字面量的方式。

var namespace1 = {
        a: function(){
            alert(1);
        },
        b: function(){
            alert(2);
        }
    };

还可以动态的创建命名空间。

//动态创建命名空间
    var MyApp = {};
    MyApp.namespace = function(name){
        var parts = name.split('.');
        var current = MyApp;
        for(var i in parts){
            if(!current[parts[i]]){
                current[parts[i]] = {};
            }
            current = current[parts[i]];
        }
    };
    MyApp.namespace('event');
    MyApp.namespace('dom.style');
    console.dir(MyApp);
    //上述代码等价于
    var MyApp = {
        event:{},
        dom:{
            style:{}
        }
    };

2.使用闭包封转私有变量

 把一些变量封转在闭包的内部,只暴露一些接口跟外界通信。

5.惰性单例

惰性单例指的是在需要的时候才创建对象实例。惰性单例是单例模式的重点,这种技术在实际开发中非常有用,有用的程度可能超出了我们的想象。

以WebQQ的登录浮窗为例,介绍与全局变量结合实现惰性的单例。

<body>
    <button id="loginBtn">登录</button>
</body>
</html>
<script>
    var createLoginLayer = (function(){
        var div;
        return function(){
            if(!div){
                div = document.createElement('div');
                div.innerHTML = '我是登录浮窗';
                document.body.appendChild(div);
            }
            return div;
        }
    })();
    document.getElementById('loginBtn').onClick = function(){
        var loginLayer = createLoginLayer();
        loginLayer.style.display = 'block';
    };
</script>

上段代码仍然违反单一职责原则,创建对象和管理单例的逻辑都放在createLoginLayer对象内部。

    //改进版
    var getSingle = function(fn){
        var result;
        return function(){
            return result || (result = fn.apply(this,arguments));
        }
    };
    var createLoginLayer = function(){
        var div = document.createElement('div');
        div.innerHTML = '我是登录浮窗';
        div.style.display = 'none';
        document.body.appendChild(div);
        return div;
    };
    var createSingleLoginLayer = getSingle(createLoginLayer);
    document.getElementById('loginBtn').onClick = function(){
        var loginLayer = createSingleLoginLayer();
        loginLayer.style.display = 'block';
    };
    //创建唯一的iframe用于动态加载第三方页面
    var createSingleIframe = getSingle(function(){
        var iframe = document.createElement('iframe');
        document.body.appendChild(iframe);
        return iframe;
    });
    document.getElementById('loginBtn').onClick = function(){
        var loginLayer = createSingleIframe();
        loginLayer.src = 'http://baidu.com';
    };

在这个例子中,我们把创建实例对象的职责和管理单例的职责分别放置在两个方法里,这两个方法可以独立变化而互不影响,当它们连接在一起的时候,就完成了创建唯一实例对象的功能,看起来是一件挺奇妙的事情。

小结

单例模式是一种简单但非常有用的模式,特别是惰性单例模式,在合适的时候才创建对象,并且只创建唯一的一个。更奇妙的是,创建对象和管理单例的职责被分布在两个不同的方法中,这两个方法组合起来才具有单例模式的威力。

免责声明:文章转载自《JavaScript设计模式与开发实践-读书笔记(4)单例模式》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇bootstrap datetimerangeDocker 创建 mongo 容器下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

设计模式学习-单例模式

1.定义 一个类有且仅有一个实例,并且自行实例化向整个系统提供 2.类图 3.代码示例 网上最多有8中实现方式,其中包括了很多非线程安全的实现。我觉得没有必要。这里提供单例模式的两种实用实现,均为线程安全,这里推荐第一种实现,即实现了线程安全,又实现了懒加载 package com.zhaoyangwoo.singleton; /** * Creat...

设计模式(一)单例模式:2-懒汉模式(Lazy)

思想: 相比于饿汉模式,懒汉模式实际中的应用更多,因为在系统中,“被用到时再初始化”是更佳的解决方案。 设计思想与饿汉模式类似,同样是持有一个自身的引用,只是将 new 的动作延迟到 getinstance() 方法中执行。 public final classLazySingleton { private staticLazySingleton...

Java 设计模式六原则及23中常用设计模式

一、设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访...

java 单例模式

单例模式 : 一个类创建的 任何对象都是同一个只存在一个实例,都只开辟同一块内存空间 单例模式就是不管外部如何创建 都只是创建一个对象 对象操作的也是只个唯一对象 饿汉式: public classSingleton { public static voidmain(String[] args) { Pattern patternO...

单例模式——java设计模式

单例模式 目录: 一、何为单例 二、使用Java EE实现单例模式 三、使用场景 一、何为单例 确保一个类只有一个实例,并且提供了实例的一个全局访问点 **1.1 单例模式类图 **               1.2 单例模式实现 (1)简单实现 public class MySingleton1 { private static MySingleto...

Java集合-单例模式斗地主&amp;amp;Collections类的shuffle方法了解

在学完Collection接口,以及其下面的List接口,了解几种基本的集合实现类如ArrayList、LinkedList和Vector后,可以做一个简单的斗地主,这里记录一下使用ArrayList来模拟实现斗地主的组合牌洗牌发牌看牌动作。 案例分析 1. 组装54张扑克牌2. 将54张牌顺序打乱3. 三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张...