JavaScript设计模式
JavaScript设计模式
设计模式概念
经过代码设计经验总结之后设计出的一种固定解决问题的方式
设计模式作用
代码复用
(相关资料图)
保证代码可靠性
将编程工程化
更易被他人理解
设计模式的分类(W3C平台)
构造器模式,模块化模式,暴露模块模式,单例模式,中介者模式,原型模式,命令模式,外观模式,工厂模式,Mixin模式,装饰模式,亨元(Flyweight)模式,MVC模式,MVP模式,MVVM模式,组合模式,适配器模式,外观模式,观察者模式,迭代器模式,惰性初始模式,代理模式,建造者模式,...
常用设计模式
1. 单例模式
概念 :
多次操作是在同一个实例对象上实现 即第一次为创建实例对象 后面的都是在原有的实例对象上操作
优点 :
节省性能 提升执行速度
function Fn(){ // 这里自定义__obj__是为了防止和Fn内置对象名重叠 if(!Fn.__obj__){ // 这里没有这个对象存在再创建 有则不创建 // 保证单例的核心 Fn.__obj__ = {}; } Fn.__obj__.name = "admin"; return Fn.__obj__; }2. 工厂模式
概念 :
多次创建多个具有相同属性名相同方法功能的不同实例对象
工厂模式的标志 :
原料(创建基础对象) 加工(给基础对象添加属性或方法) 出厂(将基础对象返回到外部)
优点 :
相互独立 分别控制 互不干扰
function Fn(name, age){ this.name = name; this.age = age } const f1 = new Fn("admin", 18) const f2 = new Fn("root", 19) // 工厂模式和单例模式不同 每次创建的都是新实例对象 console.log(f1 === f2); // false3. 抽象工厂模式
概念 :
在工厂模式的基础上进行二次封装,将相同的属性值再次封装
// 造车厂 function CreateCar(brand, color, type){ this.brand = brand; this.color = color; this.type = type; } // 专门用来生产比亚迪的生产线 function BYDCar(color, type){ return new CreateCar("比亚迪", color, type); } const b1 = new BYDCar("白色", "SUV"); const b2 = new BYDCar("红色", "轿车"); // 专门用来生产大众的生产线 function WCar(color, type){ return new CreateCar("大众", color, type); } const w1 = new WCar("黑色", "SUV"); const w2 = new WCar("灰色", "轿车");4. 适配器模式
概念 :
解决两个软件实体间的接口不兼容的问题,对不兼容的部分进行适配
例 : 手机没有3.5耳机插口,所以就需要增加一个转接头来完成适配功能以确保耳机的正常使用
// 创建一个手机构造器 有打电话打游戏功能 function CreatePhone(){ this.name = "手机" this.call = function(){ console.log("打电话") } this.game = function(){ console.log("玩游戏") } } // 创建一个电脑构造器 有游戏功能 function CreateComputer(){ this.name = "电脑" this.game = function(){ console.log("玩游戏") } } // 测试打电话和游戏功能 function test(obj){ obj.call(); obj.game(); } const p = new CreatePhone() const c = new CreateComputer() // 如果没有适配器 电脑因为没有打电话功能test会报错 function Adapter(obj){ // 如果没有这个功能则定义一个这个功能 if(!obj.call){ obj.call = function(){ console.log("这是" + obj.name + "没有打电话功能") } } return obj; } test(p); // 打电话 // 玩游戏 test( Adapter(c) ); // 这是电脑没有打电话功能 // 玩游戏 console.log(p) console.log(c)5. 代理模式
概念
不直接访问对象 而是提供一个中间对象(代理)来控制对这个对象的访问
生活中也有比较常见的代理模式:中介、寄卖、经纪人等等
// 发送数据方 target为接收方 name为发送方名字 function sender(target, name){ this.name = name; // 定义发送方式 this.send = function(msg){ console.log(this.name + "将" + msg + "交给了" + target.name); } } // 接收数据方 name为接收方名字 function reciver(name){ this.name = name; } // 创建"bob"实例对象用于接收数据 const s = new reciver("bob"); // 定义中间代理 target为接收方 function poster(target){ // 创建发送方实例对象 const f = new sender(s, "tom"); //message用于存储各种数据 this.message = []; this.send = function(msg){ this.message.push({ 发件人: f.name, 收件人: target.name, 物品: msg, 时间: Date.now() }) //调用发送方send方法 msg为传进来参数 f.send(msg); } } // 创建代理实例对象 const k = new poster(s); // 代理发送方tom发送数据给接收方bob k.send("一批教材"); k.send("一批教具"); k.send("一批学生"); console.log(k.message);6. 观察者模式
又称发布订阅模式(Publish/Subscribe)
概念
观察者模式定义了一种一对多的关系 让一个对象(发布者Dep)能被多个观察者(订阅者Observer)同时监听
优点
- 支持简单的广播通信 自动通知所有订阅者
- 页面载入后发布者很容易与观察者存在一种动态关联 增加了灵活性
- 发布者与观察者之间的抽象耦合关系能够单独扩展以及重用(解耦)
function MyEvent() { // 定义一个对象存储dep和数组形式的observer this.message = {}; // 绑定发布者和目标订阅者 this.binding = function(dep, observer) { if (this.message[dep]) { // 发布者存在 则加入新的订阅者 this.message[dep].push(observer); } else { // 发布者不存在 则创建一个数组 存入第一个订阅者 this.message[dep] = [observer]; } } // 绑定发布者和目标订阅者 this.unbinding = function(dep, observer) { // 发布者不存在 则返回 if (!this.message[dep]) return; // 找到订阅者在数组中的索引 var index = this.message[dep].indexOf(observer); if (index != -1) { // 如果有 则从索引位置删除他 this.message[dep].splice(index, 1); } } // 执行目标发布者的所有订阅者的行为 this.emit = function(dep) { // 发布者不存在 则返回 if (!this.message[dep]) return; // 调用所有订阅者方法 this.message[dep].forEach((val) => {val(dep);}) } } // 创建实例对象 const event = new MyEvent(); // 绑定发布者和订阅者 event.binding("bob", follower1); event.binding("bob", follower2); event.binding("tom", follower1); event.binding("tom", follower2); // 解绑发布者和目标订阅者 event.unbinding("tom", follower1); // 执行目标发布者所有订阅者行为 event.emit("bob"); event.emit("tom"); // 定义订阅者行为 function follower1(dep) { console.log("follower1订阅了" + dep) } function follower2(dep) { console.log("follower2我订阅了" + dep) }7. 策略模式
概念 :
将多个功能封装起来 定义一系列算法 并使他们能直接相互替换
优点 :
- 利用组合 委托 避免了条件语句
- 使代码更易理解和扩展
- 代码复用
// 需求: 绩效计算 // 条件选择方式 if分支随着绩效分类增多而增多 影响性能 let bonus = function (performance, salary) { if(performance == "S") { return salary * 4; } else if (performance == "A") { return salary * 3; } else if (performance == "B") return salary * 2; } // 策略模式 let calculateBonus = { "S": function ( salary ){ return salary * 4; }, "A": function ( salary ) { return salary * 3; }, "B": function ( salary ) { return salary * 2; } } function calculate(level, salary) { return calculateBonus[level](salary); } console.log(calculate("S", 20000) + "$")8. MVC模式
全名: Model View Controller 模型 视图 控制器
- M: 模型 按照要求来取出数据
- V: 视图 用户直观看到的页面
- C: 控制器 向系统发出指令的工具
优点
- 降低代码耦合
- 分工合作 提高开发效率
- 组件重用
工作流程
用户的请求提交给控制器
控制器接收到用户请求后根据用户的具体需求 调用相应的程序来处理用户的请求
控制器调用程序处理完数据后 将数据显示出来
// 定义模型 按照要求读取数据 class Model { m1() { return "Model1"; } m2() { return "Model2"; } } // 定义视图 用于数据展示 class View { v1(m) { console.log(m); } v2(m) { document.write(m); } } // 根据控制器找到对应数据 class Control { constructor() { this.m = new Model(); this.v = new View(); } c1() { let value = this.m.m1(); this.v.v1(value); } c2() { let value = this.m.m2(); this.v.v2(value); } } // 创建一个控制器实例对象 const c = new Control(); // 发出指令 c.c1(); c.c2();9. 组合模式
概念 :
把多个对象组成树状结构来表示局部与整体,使得用户可以同时操作单个对象或对象的组合
优点 :
- 组合模式可以非常方便地描述对象部分-整体层次结构
- 组合模式将一批子对象组织为树形结构 一条根命令能操作下面所有子元素
// // 枝 class Team { constructor(id) { // 组合模式核心之一 使得每个元素都能保存自己所有的子元素 this.children = []; this.ele = document.createElement("div"); this.ele.id = id; } add(child) { // 在数组中加入child元素 this.children.push(child); // 将child添加到当前实例对象对应元素(this.ele)中 this.ele.appendChild(child.ele); } remove(child) { // 找到这个child元素在数组中索引 let c = this.children.indexOf(child); // 根据索引删除元素 this.children.splice(c, 1); // child为实例对象 找到实例对象对应元素 并删除节点元素 child.ele.remove(); } addBorder() { // 给当前实例对象对应元素添加边框 this.ele.style.border = "2px solid black"; // 以下递归和核心之一 // 给当前实例对象children数组(保存所有子元素对应实例对象)中所有元素也添加边框 如果子实例对象对应实例对象中还有实例对象则递归进去 重复之前步骤 直到所有子孙元素 this.children.forEach(val => {val.addBorder()}); } removeBorder() { // 给当前实例对象对应元素删除边框 this.ele.style.border = "none"; // 原理同addBorder this.children.forEach(val => {val.removeBorder()}); } } // 叶 class Item { constructor(src) { // 此处ele为核心之一 这里的ele必须和上面枝中的ele相同 如果不同 this.ele在递归到叶中实例对象时 因找不到ele元素而报错而报错 this.ele = document.createElement("img"); this.ele.src = src; } add() { console.log("此为叶节点 不能添加"); } remove() { console.log("此为叶节点 不能删除"); } addBorder() { // 叶没有子元素 所以只要给自己添加边框 this.ele.style.border = "2px solid red"; } removeBorder() { // 叶没有子元素 所以只要给自己删除边框 this.ele.style.border = "none"; } } // 创建叶实例对象 const img1 = new Item("https://t7.baidu.com/it/u=1595072465,3644073269&fm=193&f=GIF"); const img2 = new Item("https://t7.baidu.com/it/u=4198287529,2774471735&fm=193&f=GIF"); const img3 = new Item("https://t7.baidu.com/it/u=2511982910,2454873241&fm=193&f=GIF"); const img4 = new Item("https://t7.baidu.com/it/u=3435942975,1552946865&fm=193&f=GIF"); // 创建枝对象 const box1 = new Team("box1"); const box2 = new Team("box2"); const box3 = new Team("box3"); const box4 = new Team("box4"); const box5 = new Team("box5"); // 将盒子 和 img组合 box1.add(box2); box1.add(box3); box3.add(box4); box4.add(box5); box1.add(img1); box4.add(img2); box4.add(img3); box5.add(img3); // 以下为box在body中排列 // box1 // box2 // box3 // box4 // box5 // img3 // img2 // img3 // img1 // 将根box1添加到body中 document.body.appendChild(box1.ele); // 给元素添加删除边框 box1.addBorder(); box3.addBorder(); img2.removeBorder();




(相关资料图)











































