15158846557 在线咨询 在线咨询
15158846557 在线咨询
所在位置: 首页 > 营销资讯 > 网站运营 > web前端之设计模式(二)

web前端之设计模式(二)

时间:2023-09-04 02:54:01 | 来源:网站运营

时间:2023-09-04 02:54:01 来源:网站运营

web前端之设计模式(二):

一、设计模式

1.1 观察者模式

定义:观察者模式,又叫发布订阅者模式,又叫消息系统,又叫消息机制,又叫自定义事件,解决主体与观察者之间的耦合问题

观察者模式是一个行为型设计模式

特点

1 解决的是耦合问题(类与类之间,对象之间,类与对象之间,模块之间)

2 对于任何一个观察者来说,其它观察者的改变不会影响自身

3 对于任何一个对象来说,既可以是观察者,也可以是被观察者

如:jQuery中的观察者模式。$.CallBacks()方法执行的结果得到一个观察者对象,

观察者对象有一个方法叫add,用来订阅消息的。观察对象有一个方法叫fire,用来发布消息

实现观察者模式

观者者对象必须具备两个方法

on 用来注册消息

第一个参数表示消息的名称。

第二个参数表示回调函数

trigger 用来触发消息

第一个参数表示消息的名称。

从第二个参数开始表示传递数据

off 用来移除消息的方法,

参数同register

once 单次订阅方法,

参数同register

通过闭包将接口返回,那么on和trigger对用户来说就是可访问的,闭包里面存储消息队列,对用户来说就是不可见的,因此是安全。

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <script> // 观察者模式又叫发布订阅者模式,消息系统,消息机制,自定义事件 var Observer = (function() { // 消息队列 var _msg = {}; // 暴露接口 return { /*** * 订阅消息 * @type 消息名称 * fn 回调函数 * 将消息回调函数,存储在消息队列中 **/ on: function(type, fn) { // 判断这种类型的消息是否存在 if (_msg[type]) { // 继续存储 _msg[type].push(fn) } else { // 初始化一个消息管道 _msg[type] = [fn] } // console.log(_msg); }, /*** * 发布消息 * @type 消息类型 * 从第二个参数开始,表示执行消息回调函数的时候,传递的参数 * 遍历该类型的消息回调函数,并逐一执行 **/ trigger: function(type) { // 该类型的消息是否存在 if (_msg[type]) { // 获取传递的参数 var args = Array.prototype.slice.call(arguments, 1); // console.log(args); // 遍历消息管道,逐一执行每一个方法 for(var i = 0; i < _msg[type].length; i++) { // 执行每一个函数, // args是一个数组,要逐一传递,因此要借助apply _msg[type][i].apply(null, args) // _msg[type][i](args) } } }, /*** * 注销消息 * @type 消息名称 * fn 回调函数 注意:匿名函数无法注销 * 从消息队列中,移除该回调函数 **/ off: function(type, fn) { // 没有传递参数,情况消息队列 // 传递了type,情况该类型的消息管道 // 传递了type和fn,从该类型的消息管道中,找到fn,并将其移除 // 没有传递参数,情况消息队列 if (type === undefined) { _msg = {}; return; } // 是否有fn if (fn) { // 传递了type和fn,从该类型的消息管道中,找到fn,并将其移除 for (var i = _msg[type].length - 1; i >= 0; i--) { // 寻找fn if (_msg[type][i] === fn) { // 移除fn, 从后向前遍历时候,从数组中删除成员不会影响遍历 _msg[type].splice(i, 1) // 删除了,不需要继续遍历了。 return; } } } else { // 传递了type,情况该类型的消息管道 _msg[type] = []; } }, /*** * 单次订阅的 * @type 消息名称 * fn 回调函数 * 注册后将其移除 **/ once: function(type, fn) { // this.on(type, fn) // this.off(type, fn) // 缓存this var me = this; // 包装函数 function callback() { // 注销回调函数, 防止无限循环。 me.off(type, callback) // 执行回调函数,并传递参数 // apply第二个参数可以是数组,也可以是类数组对象。 fn.apply(null, arguments); // console.log('执行了') } // 订阅 this.on(type, callback) } } })() // 观察者模式问题:一定是先订阅,再发布。 Observer.trigger('test', 100, 200) // 单次订阅 Observer.once('test', function() { console.log('run test', arguments); Observer.trigger('test', 'hello', 'ickt') }) // Observer.trigger('test', 100, 200) // Observer.trigger('test', true, false) // Observer.trigger('test', 'hello', 'ickt') // function demo() { // // console.log('helle ickt', this) // console.log('helle ickt') // } // // 订阅消息 // Observer.on('ickt', function(num1, num2, bool, msg) { // // console.log(arguments) // console.log(num1, num2, bool, msg); // }) // Observer.on('ickt', demo) // Observer.on('demo', function() { // console.log(arguments) // }) // // 清空 // // Observer.off(); // // Observer.off('ickt'); // // 没办法注销匿名函数 // // Observer.off('ickt', function() { // // // console.log('helle ickt', this) // // console.log('helle ickt') // // }); // // Observer.off('ickt', demo) // // 发布消息 // Observer.trigger('ickt', 100, 200, true, 'hello') // Observer.trigger('ickt', 100, 200, true, 'hello') // Observer.trigger('test', 100, 200) // Observer.trigger('demo', 100, 200) </script></body></html>


1.2 组合模式

又叫部分-整体模式,将对象组装成一个树形结构来表达这个整体,不论是部分还是整体,在表现上具有一致性。是结构型设计模式。其特点

是一个拆分合并过程。为我们提供清晰的组成结构,

通过对基对象的属性方法的继承,使成员对象间的基本表现,行为统一

成员对象的结构简单而又单一,这给我们带来了更多的组合方式。

组合模式实现步骤

对整体拆分 -> 得到不同层级的个体。 对个体组装 -> 组合得到不同的整体

所有个体都会继承同一个基类

通常基类是只能被继承,不会去实例化的的 (包含的是所有个体共有的属性方法)

新闻模块

整个新闻模块是一个根节点

每一行是一个枝干节点

每一行有多条新闻,每一条新闻就是一个叶子节点(因此不能包含子节点)

总结

是一个结构型设计模式

本质就是一个拆分合并的过程

整体与个体之间具有行为的一致性

对个体的不同的组装,可以是整体差异化

组合模式使整体结构很清晰

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> a:hover { color: #f90; } a { margin-right: 20px; } </style></head><body> <script src="./ickt.js"></script> <script> // 定义基类,让所有类继承 function Base() { // 当前元素 this.element = null; // 存储子类 this.children = []; } // 定义这些行文方法 Base.prototype.init = function() { // 基类不能初始化 throw new Error('基类不能初始化') } // 获取当前实例对应的元素 Base.prototype.getElement = function() { return this.element; } // 添加子元素 Base.prototype.add = function(child) { // 存储子对象 this.children.push(child); // 处理dom this.element.appendChild(child.getElement()); // 链式调用 return this; } // 容器类 function Container(id, parent) { // 构造函数式继承 Base.call(this); // 存储数据 this.id = id; this.parent = parent; // 初始化 this.init(); } // 可以使用类式继承,也可以使用寄生式继承 // Container.prototype = new Base(); _.inherit(Container, Base); // 重写方法 // 初始化方法 Container.prototype.init = function() { // 创建元素 this.element = document.createElement('ul'); // 添加id this.element.id = this.id; // 添加类 this.element.className = 'container' } // 显示容器 Container.prototype.show = function() { // 让容器元素上树 this.parent.appendChild(this.element); } // 每一行的类 function Item(className) { // 构造函数继承 Base.call(this); // 存储数据 this.className = className || 'item'; // 初始化 this.init(); } // 继承 _.inherit(Item, Base); // 重写方法 Item.prototype.init = function() { // 创建元素 this.element = document.createElement('li'); // 设置属性 this.element.className = this.className; } // 没有分类的新闻 function TitleNews(text, href) { // 构造函数继承 Base.call(this); // 存储数据 this.text = text; this.href = href; // 初始化 this.init(); } // 继承 _.inherit(TitleNews, Base) // 重写方法 // 初始化方法 TitleNews.prototype.init = function() { // 定义元素 this.element = document.createElement('a'); // 设置属性 this.element.href = this.href; // 设置内容 this.element.innerHTML = this.text; } // 分类新闻 function TypeNews(text, href, type) { // 构造函数继承 Base.call(this); // 存储数据 this.text = text; this.href = href; this.type = type; // 初始化 this.init(); } // 继承 _.inherit(TypeNews, Base) // 重写方法 // 初始化方法 TypeNews.prototype.init = function() { // 创建元素 this.element = document.createElement('a'); var span = document.createElement('span'); var text = document.createTextNode(this.text); // 设置内容 span.innerHTML = this.type + ' | '; // 设置属性 this.element.href = this.href; // 组装 this.element.appendChild(span) this.element.appendChild(text) } // 使用类 new Container('sport', document.body) .add( new Item() .add( new TitleNews('国安官宣与池忠国完成续约', 'www.baidu.com') ) .add( new TitleNews('足协明确外援出场规则', 'www.baidu.com') ) ) .add( new Item() .add( new TitleNews('180万!詹皇球星卡拍卖创纪录', 'www.baidu.com') ) ) .add( new Item() .add( new TypeNews('沈梦辰时髦穿搭 赵丽颖夏日针织', '#demo', '时尚') ) ) .add( new Item() .add( new TypeNews('影院归来票房超350万:赔本攒人气 但总要开始', '#demo', '科技') ) ) // 上树 .show(); // 构造一个新模块 new Container('car', document.body) .add( new Item() .add( new TitleNews('小鹏汽车完成C+轮近5亿美元融资', '#demo') ) ) .add( new Item() .add( new TypeNews('理想汽车计划7月31日在纳斯达克挂牌上市', '#demo', '汽车') ) ) .show(); </script></body></html>


1.3 策略模式

定义:将一组算法封装起来,使其彼此之间可以相互替换,封装的算法具有独立性,不会随着客户端的变换而变化。。

行为型设计模式

特点

创建的一系列算法,每组算法的业务逻辑是相同的,但是处理的过程以及结果是不同的。

算法是独立的,可以相互替换,解决了算法与使用者之间的耦合问题。

算法之间的独立性使其方便进行单元测试。

应用:jQuery的动画算法就是一个策略模式,还有在处理商品促销价格,以及表单校验的时候,也可以使用策略模式。

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <input type="text"> <script> // 封装校验算法 var strage = (function() { // 校验算法 var methods = { // 校验数字 number: function(value) { // 判断数组 if (!/^/d+$/.test(value)) { // 不是数字 return '不是数字' } }, // 校验内容 en: function(value) { // 判断数组 if (!/^[a-zA-Z]+$/.test(value)) { // 不是数字 return '不都是英文' } } } // 接口方法 return { // 校验方法 use: function(type, value) { // 找到校验策略,校验数据 return methods[type] && methods[type](value) }, // 添加策略 add: function(type, fn) { // 存储校验策略 methods[type] = fn; } } })() // 输入框输入完成 var inp = document.getElementsByTagName('input')[0]; // 失去焦点 inp.onblur = function(e) { // 校验 // var result = strage.use('number', e.target.value) // var result = strage.use('en', e.target.value) // 添加校验方法 strage.add('nickname', function(value) { // 校验 if (!/^/w{2,8}$/.test(value)) { // 提示错误 return '用户名是2到8为的字母数字下划线'; } }) var result = strage.use('nickname', e.target.value) console.log(result); } </script></body></html>


1.4 命令模式

定义:将请求与实现解耦并封装成独立的对象,从而使不同的请求对客户端实现的参数化。

行为型设计模式

特点

将执行的命令封装,解决命令的发起者与命令的执行者之间的耦合

使用者不必了解每条命令的接口是如何实现的,命令是如何执行的。

所有命令都被存储在命令对象上。

命令的使用具有一致性,多数命令在一定程度上简化了操作方法的实现,

对命令的封装,使得每次执行时都要调用一次命令对象,增加了系统的复杂度

canvas 绘图

绘制圆的过程中遇到哪些问题?

1 源生的API有时候不好用

绘制一个圆写了5行代码

绘制一个圆,只需要圆心坐标,半径,颜色就够了,其他的都多余

2 如果将ctx变量放在全局环境中,有风险,可能会被人更改

3 如果将ctx变量保存在闭包中,外界就无法访问了

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body style="text-align: center;"> <canvas width="1000" height="600" style="border: 1px solid pink"></canvas> <script> // // 获取canvas // var canvas = document.getElementsByTagName('canvas')[0]; // // 设置样式 // canvas.style.border = '1px solid pink'; // // 绘制圆 // var ctx = canvas.getContext('2d'); // // 绘制元素 // ctx.beginPath(); // // 绘制圆 // ctx.arc(200, 200, 100, 0, Math.PI * 2); // ctx.closePath(); // // 设置颜色 // ctx.strokeStyle = 'green'; // ctx.stroke(); // 分装一个指令对象 var Command = (function() { // 获取canvas var canvas = document.getElementsByTagName('canvas')[0]; // 绘制圆 var ctx = canvas.getContext('2d'); // 封装指令 var _C = { // 描边圆 strokeCircle: function(x, y, r, color) { ctx.beginPath(); ctx.arc(x, y, r, 0, 2 * Math.PI); ctx.closePath(); // 设置颜色并描边 ctx.strokeStyle = color; ctx.stroke(); }, // 填充元素 fillCircle: function(x, y, r, color) { ctx.beginPath(); ctx.arc(x, y, r, 0, 2 * Math.PI); ctx.closePath(); // 设置颜色并描边 ctx.fillStyle = color; ctx.fill(); }, // 绘制矩形 fillCenterRect: function(x, y, width, height, color) { // 传递了颜色,设置颜色 if (color) { ctx.fillStyle = color; } // 绘制 ctx.fillRect(x - width / 2, y - height / 2, width, height) } } // 暴露接口 return { // 执行方法 exec: function(type) { // 获取第二个参数开始,传递的参数 var args = Array.prototype.slice.call(arguments, 1); // 执行指令 _C[type] && _C[type].apply(null, args); // 链式调用 return this; } } })() // 绘制圆 Command.exec('fillCircle', 100, 100, 50, 'green') Command.exec('strokeCircle', 300, 300, 50, 'pink') // 还可以链式调用 .exec('fillCircle', 300, 100, 50, 'green') .exec('fillCenterRect', 400, 200, 100, 100, 'orange') </script></body></html>

关键词:模式,设计

74
73
25
news

版权所有© 亿企邦 1997-2025 保留一切法律许可权利。

为了最佳展示效果,本站不支持IE9及以下版本的浏览器,建议您使用谷歌Chrome浏览器。 点击下载Chrome浏览器
关闭