Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript设计模式----单例模式 #18

Open
zhiqiang21 opened this issue Jul 23, 2016 · 4 comments
Open

JavaScript设计模式----单例模式 #18

zhiqiang21 opened this issue Jul 23, 2016 · 4 comments

Comments

@zhiqiang21
Copy link
Owner

zhiqiang21 commented Jul 23, 2016

声明:这个系列为阅读《JavaScript设计模式与开发实践》 ----曾探@著一书的读书笔记

目录:

1.单例模式的特点和定义

保证一个类仅有一个实例,并且提供一个访问它的全局访问点。

2.传统面向对象语言的单例模式

2.1传统语言描述的单例模式JavaScript实现

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('seven1');
var b = Singleton.getInstance('seven2');

或者使用闭包的形式创建单例模式,同时符合惰性单例的特性

var Singleton = function(name) {
    this.name = name;
};

Singleton.prototype.getName = function() {
    alert(this.name);
};

//利用闭包的特性创建单例,同时符合惰性单例的特性
Singleton.getInstance = (function(name) {
    var instance;
    return function(name){
        if (!instance) {
            instance = new Singleton(name);
        }
    }
})();

var a = Singleton.getInstance('seven1');
var b = Singleton.getInstance('seven2');

console.log(a===b);   //true

2.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('seven1');
var b = new CreateDiv('seven2');

这样编写单例模式的缺点:

为了把instance封装起来,我们使用了自执行的匿名函数和闭包,并且让这个匿名函数返回真正的Singleton构造方法,这增加了一些程序的复杂度。

CreateDiv的构造函数负责了两件事情。1.创建对像和执行初始化init方法,第二是保证只有一个对象。不符合设计模式中的单一职责的概念。

2.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('seven1');
var b = new ProxySingletonCreateDiv('seven2');

引入代理实现单例模式的特点:

我们负责管理单例的逻辑移到了代理类ProxySingletonCreateDiv中。这样一来,CreateDiv就变成了一个普通的类,他跟ProxySingletonCreateDiv组合起来可以达到单例模式的效果。

3.JavaScript的单例模式对比

在以上的代码中实现的单例模式都混入了传统面向对象语言的特点。而没有利用JavaScript这们语言的特点来实现一个单例模式。

3.1惰性单例的例子

概念描述:

惰性单例指的是在需要的时候才创建对象的实例。惰性单例是单例模式的重点。

var createLoginLayer=(function(){
    var div;
    return function(){
        if(!div){
            div=document.createElement('div');
            //创建一个登录框
        }
        return div;
    }
})();

document.getElementById('loginBtn').onclick=function(){
    var loginLayer=createLoginLayer();
    loginLayer.style.display='block';
};

代码解析:
这里的对惰性单例的实现主要是只有单例了网页上的登录按钮,才会去创建,登录框的dom节点,并且只是创建一次。

3.2通用的单例模式例子

根据3.1的代码示例,我们的单例对像,但是并不是通用的,比如我们要创建的不是div而是iframe,那要怎么办呢?

//获取单例
var getSingle = function(fn){
    var result;
    return function (){
        return result || (result=fn.apply(this,arguments));
    };
};

//创建div登录框
var createLoginLayer=function (){
    var div= document.createElement('div');
    div.innerHTML='我是登录框';
    document.body.appendChild(div);
    return div;
};

//创建iframe的dom节点
var createIframe=function(){
    //创建irame节点的代码
}

var createSingleLoginLayer = getSingle(createLoginLayer);
var createSingleIframe=getSingle(createIframe);

var loginLayer1 = createSingleLoginLayer();
var loginLayer2 = createSingleLoginLayer();

var iframe1=createSingleIframe();
var iframe2=createSingleIframe();

console.log(loginLayer1 === loginLayer2);

通用的单例创建的例子就是通过封装一个getSingle需要实现单例模式的对象。而且只是会只创建一次。因为使用了闭包的原因通过getSingle创建的result会在内存中一直存在不会销毁(除非页面关闭,或者手动释放)。

总结:

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

@harrisoff
Copy link

2.1 里面 Singleton() 构造函数的 this.instance 没有用吧?

@zhiqiang21
Copy link
Owner Author

@harrisoff 这里是显式的声明Singleton类有一个instance属性,可以获取单例。声明的时候给了一个默认是值Null。

@harrisoff
Copy link

@zhiqiang21 我的看法是这样的:

var Singleton = function(name) {
    this.name = name;
    // 这个 this 是 Singleton 的实例
    this.instance = null;
};
// ...
Singleton.getInstance = function(name) {
    // 这里的 this 指向的是 Singleton 这个函数
    if (!this.instance) {
        // 也就是说这个 this.instance 成为了 Singleton 的一个静态属性
        // 跟上面的 this.instance = null 的 instance 不是一个东西
        this.instance = new Singleton(name);
    }
    return this.instance;
};

@SHERlocked93
Copy link

@zhiqiang21 我的看法是这样的:

var Singleton = function(name) {
    this.name = name;
    // 这个 this 是 Singleton 的实例
    this.instance = null;
};
// ...
Singleton.getInstance = function(name) {
    // 这里的 this 指向的是 Singleton 这个函数
    if (!this.instance) {
        // 也就是说这个 this.instance 成为了 Singleton 的一个静态属性
        // 跟上面的 this.instance = null 的 instance 不是一个东西
        this.instance = new Singleton(name);
    }
    return this.instance;
};

我觉得你说的是对的,如果在构造函数里使用 this.instance = null 这个 instance 是实例上的,而 getInstance 时是 Singleton 构造函数

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants