JavaScript十大设计模式详解


一、工厂模式

定义:类似工厂批量生产相同规格的产品,解决对象重复创建问题。

核心:通过函数封装对象的创建过程,调用时无需使用new

优点:减少重复代码,简化对象创建流程

缺点:无法识别对象的具体类型

function CreatePerson(name, age, sex) {
    var obj = {};
    obj.name = name;
    obj.age = age;
    obj.sex = sex;
    obj.sayName = function () {
        return this.name;
    };
    return obj;
}
var p1 = CreatePerson("张三", 20, "男");

二、单体模式

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

核心:通过变量标识实例是否已创建,已创建则直接返回。

优点

  • 划分命名空间,减少全局变量
  • 实例只创建一次,保证一致性

缺点

  • 扩展性差,增加新属性需要修改原代码
  • 职责过重,违反单一职责原则
var Singleton = (function () {
    var instance = null;
    function CreateSingleton(name) {
        this.name = name;
    }
    return function (name) {
        if (!instance) {
            instance = new CreateSingleton(name);
        }
        return instance;
    };
})();

应用场景:弹窗、缓存、登录窗口等只需一个实例的场景


三、模块模式

定义:为单体模式添加私有变量和私有方法,减少全局污染。

核心:利用闭包封装私有成员,返回公有接口访问。

适用场景:需要初始化数据并公开访问方法的场景

var singleMode = (function () {
    var privateNum = 112; // 私有变量
    function privateFunc() {} // 私有方法

    return {
        publicMethod1: function () {},
        publicMethod2: function () {},
    };
})();

增强模块模式:实例化特定类型并扩展其属性方法


四、代理模式

定义:为一个对象提供一个替身,控制对本体对象的访问。

核心:代理对象与本体对象实现同一接口,将调用传递给本体。

优点

  • 控制本体对象的访问时机
  • 推迟本体对象实例化(延迟加载)
var RealImage = function (fileName) {
    this.fileName = fileName;
    this.loadFromDisk = function () {
        console.log("加载文件: " + this.fileName);
    };
};

RealImage.prototype.display = function () {
    console.log("显示图片: " + this.fileName);
};

var ProxyImage = function (fileName) {
    this.realImage = null;
    this.fileName = fileName;
};

ProxyImage.prototype.display = function () {
    if (!this.realImage) {
        this.realImage = new RealImage(this.fileName);
        this.realImage.loadFromDisk();
    }
    this.realImage.display();
};

var img = new ProxyImage("test_10mb.jpg");
img.display();
img.display();

应用场景:图片预加载、合并HTTP请求、延迟加载


五、职责链模式

定义:将请求的发送者和接收者解耦,使多个对象都有机会处理请求。

核心:每个接收者都包含下一个接收者的引用,形成一条链。

优点

  • 降低耦合度,发送者和接收者解耦
  • 可以动态调整链的顺序或增删节点

缺点

  • 不保证被处理,请求可能无人处理
  • 可能创建多个对象,影响性能
function order500(orderType, pay) {
    if (orderType === 1 && pay === true) {
        console.log("500元定金,预购成功");
    } else {
        return "nextSuccessor"; // 传递给下一个
    }
}

function order200(orderType, pay) {
    if (orderType === 2 && pay === true) {
        console.log("200元定金,预购成功");
    } else {
        return "nextSuccessor";
    }
}

应用场景:表单验证、事件处理、权限控制


六、命令模式

定义:将请求封装为命令对象,参数化调用者操作。

核心:命令对象包含execute()方法,调用者只关心命令的执行。

优点:解耦请求发起者与执行者、支持撤销/重做

缺点

  • 命令类数量可能过多,增加系统复杂度
  • 命令执行过程无法直观看到
var RefreshMenuBarCommand = function (receiver) {
    this.receiver = receiver;
};
RefreshMenuBarCommand.prototype.execute = function () {
    this.receiver.refresh();
};

var setCommand = function (button, command) {
    button.onclick = function () {
        command.execute();
    };
};
setCommand(b1, new RefreshMenuBarCommand(MenuBar));

宏命令:一组命令的集合,一次执行多个命令


七、模板方法模式

定义:定义算法骨架,将某些步骤延迟到子类中实现。

核心:父类定义算法框架(模板),子类实现具体步骤。

优点:代码复用、扩展性强、符合开闭原则

缺点

  • 类数目增加,系统复杂度增加
  • 继承本身会破坏封装性
var Interview = function () {};
Interview.prototype.writtenTest = function () {};
Interview.prototype.technicalInterview = function () {};
Interview.prototype.leader = function () {};
Interview.prototype.waitNotice = function () {};

Interview.prototype.init = function () {
    this.writtenTest();
    this.technicalInterview();
    this.leader();
    this.waitNotice();
};

var BaiDuInterview = function () {};
BaiDuInterview.prototype = new Interview();
BaiDuInterview.prototype.writtenTest = function () {
    console.log("百度笔试题目");
};

应用场景:框架搭建、算法框架、组件继承


八、策略模式

定义:定义一系列算法,将每个算法封装起来,使它们可以互换。

核心:策略对象负责算法实现,上下文负责委托执行。

优点

  • 消除大量if-else语句
  • 符合开闭原则
  • 算法可复用

缺点

  • 策略类数量可能过多
  • 必须了解所有策略的区别
var strategies = {
    A: function (salary) {
        return salary * 4;
    },
    B: function (salary) {
        return salary * 3;
    },
    C: function (salary) {
        return salary * 2;
    },
};

var calculateBouns = function (level, salary) {
    return strategies[level](salary);
};

应用场景:表单验证、动画缓动、促销活动计算


九、发布订阅模式

定义:定义对象间一对多的依赖关系,当对象状态变化时,所有依赖者收到通知。

核心:发布者不直接通知订阅者,通过消息代理中间层转发。

优点

  • 实现松耦合,发布者和订阅者不直接关联
  • 支持广播通信

缺点

  • 订阅者无法知道消息何时被处理
  • 可能导致订阅者数量过多

三要素:发布者、订阅者、消息代理

var Event = (function () {
    var list = {};
    return {
        listen: function (key, fn) {
            list[key] = list[key] || [];
            list[key].push(fn);
        },
        trigger: function () {
            var key = Array.prototype.shift.call(arguments);
            var fns = list[key];
            if (!fns) return;
            for (var i = 0; i < fns.length; i++) {
                var fn = fns[i];
                fn.apply(this, arguments);
            }
        },
        remove: function (key, fn) {},
    };
})();

Event.listen("color", function (size) {
    console.log("尺码:" + size);
});
Event.trigger("color", 42);

应用场景:组件通信、模块间通信、异步编程、状态管理


十、中介者模式

定义:用一个中介对象封装一系列对象交互,使对象间耦合松散。

核心:所有对象只与中介者通信,中介者负责协调处理。

优点:解除对象间耦合,便于维护和扩展

缺点

  • 中介者可能变得过于复杂,成为系统瓶颈
  • 过度集中管理可能导致中介者职责不清
var Player = function (name, teamColor) {
    this.name = name;
    this.teamColor = teamColor;
    this.state = "alive";
};

Player.prototype.win = function () {
    console.log(this.name + " 胜利");
};

Player.prototype.lose = function () {
    console.log(this.name + " 失败");
};

Player.prototype.die = function () {
    this.state = "dead";
    playerDirector.receiveMessage("playerDead", this);
};

var playerDirector = (function () {
    var players = {};
    var operations = {};

    operations.addPlayer = function (player) {
        var teamColor = player.teamColor;
        players[teamColor] = players[teamColor] || [];
        players[teamColor].push(player);
    };

    operations.playerDead = function (player) {
        var teamColor = player.teamColor;
        var teamPlayers = players[teamColor];
        var allDead = true;

        for (var i = 0; i < teamPlayers.length; i++) {
            if (teamPlayers[i].state !== "dead") {
                allDead = false;
                break;
            }
        }

        if (allDead) {
            for (var i = 0; i < teamPlayers.length; i++) {
                teamPlayers[i].lose();
            }
            for (var color in players) {
                if (color !== teamColor) {
                    var otherTeam = players[color];
                    for (var j = 0; j < otherTeam.length; j++) {
                        otherTeam[j].win();
                    }
                }
            }
        }
    };

    return {
        addPlayer: function (player) {
            operations.addPlayer(player);
        },
        receiveMessage: function (message, player) {
            operations[message](player);
        },
    };
})();

var player1 = new Player("玩家A", "红队");
var player2 = new Player("玩家B", "红队");
playerDirector.addPlayer(player1);
playerDirector.addPlayer(player2);

应用场景:游戏玩家管理、表单联动、购物车逻辑


十一、JavaScript 设计模式对比总结

模式 核心问题 关键词
工厂模式 对象创建 new、批量生产
单体模式 实例唯一 全局唯一
模块模式 封装私有 闭包、暴露接口
代理模式 访问控制 替身、延迟加载
职责链模式 请求传递 链式处理
命令模式 请求封装 execute()、撤销
模板方法模式 算法骨架 继承、框架
策略模式 算法封装 互换、委托
发布订阅模式 消息通信 订阅、发布、Broker
中介者模式 对象协调 中心化、解耦

文章作者: 弈心
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 弈心 !
评论
  目录