JavaScript 设计模式之单例模式

105 阅读2分钟

单例模式顾名思义就是一个类仅有一个实例,并提供一个访问它的全局访问点。 简单的代码实现如下:

const Singleton = function (name) {
    this.name = name;
    this.instance = null;
};

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

Singleton.getInstance = function (name) {
    return this.thisinstance || (this.thisinstance = new Singleton(name));
};

let instance1 = Singleton.getInstance('instance1');
let instance2 = Singleton.getInstance('instance2');

console.log(instance1 === instance2); // true

还有一种是将实例属性隐藏起来的实现:

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

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

Singleton.getInstance = (function () {
    let instance = null;
    return function (name) {
        return instance || (instance = new Singleton(name));
    };
})();

let instance1 = Singleton.getInstance('instance1');
let instance2 = Singleton.getInstance('instance2');

console.log(instance1 === instance2); // true

以上两种代码都能通过 Singleton.getInstance 函数实现了类的唯一创建,但是一提到类或者实例,我们还是想到直接 new 操作符的方式,这种被隐藏的创建方式多多少少让我们有点 不太习惯。所以我们需要尝试实现一个可以值直接 new 的单例模式。

const Singleton = (function () {
    let instance;

    const Singleton = function (name) {
        if (instance) {
          return instance;
        }

        this.name = name;

        return instance = this;
    };

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

    return Singleton;
})();

let instance1 = new Singleton('instance1');
let instance2 = new Singleton('instance2');

console.log(instance1 === instance2); // true  

以上就是一个可以通过 new 创建实例的单例模式,如果你坚持单一职责原则的编码原则的话,也可以将以上代码职责分离,实现一个代理模式的单例模式。

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

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


const ProxySingleton = (function () {
    let instance = null;
    return function (name) {
        return instance || (instance = new Singleton(name));
    };
})();

let proxyInstance1 = new ProxySingleton('proxyInstance1');
let proxyInstance2 = new ProxySingleton('proxyInstance2');

console.log(proxyInstance1 === proxyInstance2); // true

用代理方式实现的单例模式,其实就是将基础类的实现和单例实现分离,这样的好处是基础类可以单独使用。

还可以使用 ES6 提供的 class 语法糖配合 static 关键字实现的单例模式:

class Singleton {
    constructor(name) {
        this.name = name;
    }

    getName() {
        console.log(this.name);
    }

    static getInstance(name) {
        return Singleton.instance || (Singleton.instance = new Singleton(name));
    }
}

let instance1 = Singleton.getInstance('instance1');
let instance2 = Singleton.getInstance('instance2');

console.log(instance1 === instance2);     // true

总结

“要实现一个标准的单例模式并不复杂,无非是用一个变量来标志当前是否已经为某个类创建过对象,如果是,则在下一次获取该类的实例时,直接返回之前创建的对象” --摘自《JavaScript设计模式与开发实践》,这是我学习设计模式过程中记录自己所得的一篇笔记,本文大部分知识理论来自《JavaScript设计模式与开发实践》。到目前为止是不是已经感觉到了 JavaScript 闭包的强大!!!