JavaScript设计模式实战:单例与策略模式详解

作者:代码拾荒

时间:2026-10-31

浏览:260

本文深入解析JavaScript中的单例模式与策略模式。通过创建DOM节点和年终奖计算等具体案例,演示如何保证类仅有一个实例并提供全局访问点。重点讲解惰性单例的实现,结合代理模式分离创建与管理职责,避免资源浪费。同时介绍策略模式封装算法,实现表单验证与业务逻辑的灵活替换,提升代码可维护性。

文章目录

单例模式基础与实现

一、单例模式

1、概念

确保一个类只有一个实例,并提供全局访问点

2、单例模式的实现 -- 以创建div节点为例

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('one');
var b =new createDiv('two');
console.log(a===b);  //true

上述例子存在缺陷:

(1)采用匿名自执行函数,增加了函数复杂度

(2)createDiv函数承担两项任务:创建对象与管理单例,违背单一职责原则

改进方案:使用代理实现

    //代理模式--改进
        //只负责创建div的一个类
        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 proxySingle = (function(){
            var instance;
            return function(html){
                if(!instance){
                    instance = new createDiv(html)
                }
                return instance  
            }
        })();
        var a = new proxySingle('one');
        var b = new proxySingle('two');
        console.log(a === b); //true

3、惰性单例(使用)

仅在需要时创建对象实例

结合登录功能需求,登录弹窗仅出现一次,在点击登录按钮时触发

两种实现方式

(1)页面加载完成时预先创建浮窗,初始状态隐藏,用户点击登录时显示。缺点:浪费DOM节点。

(2)用户点击登录按钮时再创建浮窗:代码如下:

var createLoginer = function(){
    var login = document.createElement("div");
    login.innerHTML='登录浮窗';
    login.style.display = 'none';
    document.body.appendChild(login);
    return login
}
//点击登陆按钮
btn.onclick = function(){
   var loginer = createLoginer();
   loginer.style.display = 'block';
}

虽达到惰性目的,但失去单例效果——再次修改如下:

使用变量判断是否已创建登录浮窗

 //创建登陆浮窗
        var createLoginer = function () {
            var login = document.createElement("div");
            login.innerHTML = '登录浮窗';
            login.style.display = 'none';
            document.body.appendChild(login);
            return login
        }
        var loginer;
        var getSingle = function (fn) {
            return function () {
                console.log(this)
                return loginer || (loginer = fn.apply(this))
            }
        }
        //createSingleLoginer接下来作为函数调用,所以getSingle得返回一个函数,并且这个函数得返回登录浮窗
        var createSingleLoginer = getSingle(createLoginer);
        //点击登陆按钮
        var btn = document.getElementById("btn");
        btn.onclick = function () {
            var loginer = createSingleLoginer();
            loginer.style.display = 'block';
        }

getSingle用于管理单例

惰性单例:仅在合适时机创建对象,且仅创建唯一实例。将创建对象与管理单例的职责分散至不同方法,组合后发挥真正单例模式威力。

策略模式与算法封装

二、策略模式

1、概念:

定义一系列算法,分别封装,并可相互替换

2、实例

年终奖发放,按等级与基础工资乘积计算

  var strage = {
            S:function(salary){
                return salary*4
            },
            A:function(salary){
                return salary*3
            },
            B:function(salary){
                return salary*2
            }
        }
        var calculateBounus = function(level,salary){
            return strage[level](salary)
        };
        console.log(calculateBounus('S',2000)) //8000
        console.log(calculateBounus('A',2000))  //6000

3、策略模式用于封装算法,更广义应用:表单验证登录算法

将校验方式封装为策略对象

 var validatorFunc = function () {
            var validator = new Validator();
            validator.add(loginForm.userName, "isEmpty", '用户名不能为空')
            validator.add(loginForm.passWord, "minLength:6", '密码长度不能少于6位')
            validator.add(loginForm.phone, "phoneValidator", '手机号码格式错误')
            var errMsg = validator.start();
            return errMsg
        }
        var loginForm = document.getElementById("login")
        function login() {
            var errMsg = validatorFunc();
            if (errMsg) {
                alert(errMsg);
                return false; //阻止表单提交
            }
        }
        var Validator = function () {
            //缓存校验规则
            this.cache = [];
        }
        Validator.prototype.add = function (dom, rule, err) {
            var ary = rule.split(':');
            this.cache.push(function () {
                var strategy = ary.shift();
                ary.unshift(dom.value);
                ary.push(err)
                return loginStrage[strategy].apply(dom, ary)
            })
        }
        Validator.prototype.start = function () {
            for (var i = 0; i < this.cache.length; i++) {
                var err = this.cache[i]()
                if (err) {
                    alert(err)
                }
            }
        }

运行页面:

单例模式基础与实现

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章