关键词:
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);
// false
3. 抽象工厂模式
概念 :
在工厂模式的基础上进行二次封装,将相同的属性值再次封装
// 造车厂
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. 组合模式
概念 :
把多个对象组成树状结构来表示局部与整体,使得用户可以同时操作单个对象或对象的组合
优点 :
- 组合模式可以非常方便地描述对象部分-整体层次结构
- 组合模式将一批子对象组织为树形结构 一条根命令能操作下面所有子元素
// <style>
// imgwidth:100px;
// *margin: 10px;padding: 10px;
// </style>
// 枝
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();