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);
// 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>
// img{width: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();
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端