JS设计模式-单例模式
发布于 5 年前 作者 myfirebug 4698 次浏览 来自 分享

单例模式 1、普通构造函数 单例模式是指一个构造函数,无论new多少次,返回的都是同一个实例,比如alert,在我们使用的时候页面上只会有一个alert弹窗。先来看一个普通的构造函数

class Test {}
let a = new Test();
let b = new Test();
console.log(a === b); // false

通过函数属性构造单例 如果我们希望a,b是完全相等的应该怎样做?
可以在构造函数上增加一个instance属性来保存实例,并增加一个getInstance静态方法来获取实例。如果实例存在则直接返回,如果不存在则创建一个保存在instance属性中并返回。

class Single {
}
Single.getInstance = function () {
    if (!this.instance) {
        this.instance = new Single();
    }
    return this.instance
}
let a = Single.getInstance();
let b = Single.getInstance();
console.log(a === b);

解决参数问题

function Dialog(config) {
    this.config = Object.assign({}, {
        title: '温馨提示',
        content: '我是内容区域'
    }, config);
}
Dialog.getInstance = function (config) {
    if (!this.instance) {
        this.instance = new Dialog(config);
    } else {
        this.apply(this.instance, arguments)
    }
    return this.instance
}
Dialog.prototype.show = function () {
    console.log(this.config.title);
}

let a = Dialog.getInstance({
    title: '001标题',
    content: '001内容'
});
a.show(); // 001标题
let b = Dialog.getInstance({
    title: '002标题',
    content: '002内容'
});
b.show(); // 002标题
console.log(a === b); // true

通过new操作符实现单例构造

let Dialog = (function () {
    let instance;
    function Dialog(config) {
        if (!instance) {
            instance = this;
        }
        instance.init(config);
        return instance;
    }
    Dialog.prototype.init = function(config) {
        this.config = Object.assign({}, {
            title: '温馨提示',
            content: '我是内容区域'
        }, config);
    }
    Dialog.prototype.show = function () {
        console.log(this.config.title);
    }
    return Dialog;
})();

let a =new Dialog({
    title: '001标题',
    content: '001内容'
});
a.show(); // 001标题
let b =new Dialog({
    title: '002标题',
    content: '002内容'
});
b.show(); // 002标题
console.log(a === b); // true

省略new操作符

let Dialog = (function () {
    let instance;

    function Dialog(config) {
        instance = instance || (
            this instanceof Dialog ? this : new Dialog(config)
        )
        instance.init(config);
        return instance;
    }

    Dialog.prototype = {
        constructor: Dialog,
        init: function (config) {
            this.config = Object.assign({}, {
                title: '温馨提示',
                content: '我是内容区域'
            }, config);
        },
        show: function () {
            console.log(this.config.title);
        }
    }
    return Dialog;
})();

let a = Dialog({
    title: '001标题',
    content: '001内容'
});
a.show(); // 001标题
let b = Dialog({
    title: '002标题',
    content: '002内容'
});
b.show(); // 002标题
console.log(a === b); // true

惰性单例 这里以上面的弹窗为例,需要解决以下几个问题,动态创建dom且随便new多少次都只是创建一次,没有任何操作时是不需要创建dom的

/**
 * 绑定事件
 * @param element
 * @param type
 * @param handler
 */
const bindEvent = (element, type, handler) => {
    if (element.addEventListener) {
        element.addEventListener(type, handler, false);
    } else if (element.attachEvent) {
        element.attachEvent('on' + type, handler);
    } else {
        element['on' + type] = handler;
    }
}

/**
 * 单例模式
 */
let single = (function () {
    let single;
    return function (fn) {
        return single || (single = fn.apply(this, arguments))
    }
})();

/**
 * 创建dom
 * @returns {HTMLElement}
 */
function createDom() {
    let dom = document.createElement('div');
    dom.setAttribute('class', 'dialog-wrapper');
    dom.style.display = 'none';
    document.body.appendChild(dom);
    return dom;
}

let Dialog = (function () {
    let instance,
        dom;

    /**
     * 创建弹窗
     * @param config
     * @returns {*}
     * @constructor
     */
    function Dialog(config) {
        instance = instance || (
            this instanceof Dialog ? this : new Dialog(config)
        )
        instance.init(config);
        return instance;
    }

    Dialog.prototype = {
        constructor: Dialog,
        init: function (config) {
            this.config = Object.assign({}, {
                title: '温馨提示',
                content: '我是内容区域',
                width: 300,
                height: 200
            }, config);
            dom = single(createDom);
            dom.style.width = this.config.width + 'px';
            dom.style.height = this.config.height + 'px';
            dom.innerHTML = `<div class="dialog-header">
                                <span class="dialog-title">${this.config.title}</span>
                                <span class="close">关闭</span>
                            </div>
                            <div class="dialog-body">${this.config.content}</div>`;
            this.eventBind();
        },
        eventBind: function () {
            let closeDom = dom.getElementsByClassName('close')[0],
                _self = this;
            bindEvent(closeDom, 'click', function(e) {
                _self.hide();
            });
        },
        show: function () {
            dom.style.display = 'block';
        },
        hide: function () {
            dom.style.display = 'none';
        }
    }
    return Dialog;
})();

let a = Dialog({
    title: '001标题',
    content: '001内容'
});
a.show();
let b = Dialog({
    title: '002标题',
    content: '002内容'
});
b.show();
console.log(a === b); // true

一个简单的弹窗就使用单例模式封装完成了

4 回复

没写完发毛线😒

问题好像有点多 正确的应该是这样吧

class Test {
  static instance?: Test

  static getInstance() {
    if (!this.instance) {
      this.instance = new Test()
    }
    return this.instance
  }
}

@chenkai0520 谁说没有写完就不能发了,后面继续完善吖~~

@xingo4 还没有写完就下班了

回到顶部