ps:结构性 (接口)
目的:通过写一个适配器,来代替替换
应用场景:面临接口不通用的问题
一:基本结构
用log代替console.log
var log = (function(){
return window.console.log
})()
ps:把原方法赋予新接口名(新方法)
二:示例
例子:框架的变更
需求:目前项目使用A框架,现在改成了B,两个框架十分类似,但是少数几个方法不同,
// 假设原框架的方法和新框架的方法是
A.c(); //$.css()
A.o(); //$.on()
window.A=$;
A.c = function(){
return $.css.apply(this.arguments);
}
A.o = function(){
return $.on.apply(this.arguments);
}
例子:参数适配
需求:为了避免参数不适配产生的问题,很多框架会有一个参数适配操作,
function f1(config){
// 默认配置
var _default={
name:'xxx',
color:'red'
}
// 用户不传就用默认配置
for(var item in _default){
_default[item] = config[item] || _default[item]
}
}
ps:当你的方法传入的参数较为复杂,最好都加上参数适配器
ps:结构性 (方法内容)
目的:不重写方法的扩展方法
应用场景:当一个方法需要扩展但又不好去修改他的方法
一:基本结构
假设有个人写了个a模块,内部有个b方法,模块为他人写好,不能修改,如何扩展b方法呢
var a = {
b:function(){
}
};
function myb(){
a.b();
// 然后继续书写扩展方法
}
ps:我们新建一个自己的方法,在内部调用b方法,并且再执行自己的方法,这样可不在修改原对象的情况下,去扩展
二:示例
例子:扩展已有的事件绑定
需求:现在项目改造,需要给input标签已有的事件增加操作
// 新建一个适配工厂,接受两个参数,一个是绑定的dom,一个是要扩展方法
var decorator = function(dom,fn){
// 判断dom上是否已经绑定click事件
if(typeof dom.onclick == "function"){
// 拿老方法
var _old = dom.onclick;
// 再绑定click事件
dom.onclick = function(){
//调用老方法和扩展方法
_old();
fn();
}
}
}
decorator(document.getElementById('dom1'),function(){
// 我需要扩展的方法具体实现
console.log(2);
})
ps:没必要去找到老代码在哪去改写,直接传入我需要改动的dom和扩展fn
例子:Vue的数组监听
需求:Vue中利用definePropert可以监听对象,那数组怎么办
// 又不能去改写数组原有的方法,先将数组原有的方法拷贝一份
var arrayProto = Array.prototype;
var arrayMethods = Object.create(arrayProto);
// 定义哪些方法会触发数组更新
var methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
];
methodsToPatch.forEach((method)=>{
// 先拿出老方法
var original = arrayProto[method];
// 调用老方法
var result = original.apply(this,arguments);
// 加上我们要调用的新方法
dep.notify();
return result;
})
ps:行为型
目的:解耦实现和调用,让双方互不干扰,
应用场景:调用的命令充满不确定性
一:基本结构
var command = (function(){
var action = {
// 这里是具体的实现
};
return function excute(){
// 这里是供外部调用的方法,在此可以拿到action的实现
}
})();
ps:调用的时候只需要给到command命令,excute去解析命令并调用具体的实现
二:示例
例子:绘图命令
需求:封装一个canvas绘图库
var canvasCommand = (function(){
var action = {
drawCircle:function(){
},
drawRect:function(){
}
}
return function excute (commander){
commander.forEach((item)=>{
action[item.command](item.config);
})
}
})();
// 调用只需要传入需要绘制的图形数组
canvasCommand([
{
command:'drawRect',
config:{},
},{
command:'drawRect',
config:{},
}
])
例子:绘制随机数量的图片
需求:需要做一个画廊,图片数量和排列顺序随机
// 创建图片html=>展示
var createImg = (function(){
var action = {
create:function(){
var htmlArr=[];
var _htmlstring = '';
var _htmlTemplate="{{title}}
";
//行为型-->策略模式,图片排序方式
var displayWay={
normal:function(arr){
return arr;
},
reverse:function(arr){
return arr.reverse;
}
}
obj.imgArr.forEach((img)=>{
var _html;
_html=_htmlTemplate.replace('{{img-url}}',img.img).replace('{{title}}',img.title);
htmlArr.push(_html);
})
htmlArr=displayWay[obj.type](htmlArr);
_htmlstring=htmlArr.join("");
return ""+_htmlstring+"";
},
display:function(obj){
var _html=this.create(obj);
obj.target.innerHTML=_html;
}
};
return function excute (obj){
// 结构性-->适配器模式,obj可能极其复杂,先适配器默认参数
var _default = {
imgArr:[{img:'xxx',title:'default title'}],
type:'normal',
target.document.body
};
for(var item in _default){
_default[item] = obj[item] || _default[item];
};
action.display(_default);
}
})()
createImg({
imgArr:[{img:'xxxx',title:'default title1'},{img:'xxxx',title:'default title2'}],
type:'normal',
})
ps:
用户只管输入命令,不用关心api ,
命令和实现解耦
数据-->直接调用api
变成
数据-->excute命令解析层-->调用api
ps:行为型 (事件绑定)
目的:减少对象间的耦合,来提高扩展性
应用场景:当两个模块直接沟通会增加他们的耦合时
一:基本结构(模式不是固定的,需要记住的是思维)
// 定义一个观察者
function observe(){
this.message={
// 这个对象储存我监听的事件
}
}
// 注册事件
observe.prototype.regist = function(type,fn){
this.message[type] = fn;
}
// 执行事件
observe.prototype.fire = function(type,fn){
this.message[type]();
}
// 删除事件
observe.prototype.remove = function(type,fn){
this.message[type] = null;
}
ps:定义一个中转观察者,两个模块之间不直接沟通,而是通过观察者 一般适用于不方便直接沟通,或者异步操作
二:示例
例子:多人合作问题
需求:现在假设A工程师写了首页模块,B工程师写了评论模块.现在要把评论展示在首页
// 定义一个观察者
function observe(){
this.message = {};
}
// 注册事件
observe.prototype.regist = function(type,fn){
this.message[type] = fn;
}
// 触发
observe.prototype.fire = function(type,fn){
this.message[type]()
}
var observeOb = new observe();
// 假设首页需要热评
function comment(){
this.commentList = [
{
type:'hot',
content:'xxxx'
}
];
// 注册事件,把需要的数据给出去
var self = this;
observeOb.regist('getHot',function(){
var _arr = [];
self.commentList.forEach((item)=>{
if(item.type=='hot'){
_arr.push(item)
}
})
return _arr;
})
}
function index(){
// 执行注册的事件就可以拿到需要的数据
var _arr = observeOb.fire('getHot');
}
例子:一个转盘
需求:一个转盘应用,每转一圈速度会变慢
// 定义一个观察者
function observe(){
this.message={
// 这个对象储存我监听的事件
}
}
// 注册事件
observe.prototype.regist = function(type,fn){
this.message[type] = fn;
}
// 执行事件
observe.prototype.fire = function(type,fn){
this.message[type]();
}
// 删除事件
observe.prototype.remove = function(type,fn){
this.message[type] = null;
}
// 初始化html=>最终结果选定=>运动效果=>运动控制
var _domArr = [];
function htmlInit(target){
// 初始化10个奖品
for(var i=0;i<9;i++){
// 每次循环创建一个div
var _div = document.createElement('div');
_div.innerHTML = i;
// 初始样式
_div.setAttribute('class','item');
// 放进target
target.appendChild(_div);
_domArr.push(_div);
}
}
// 结果模块
function getFinal(){
var _num = math.random()*10+40;//基础转四圈,停留在哪一个用随机数
return Math.floor(_num,0);
}
// 动画模块
//moverConfig={moveTime:10移动次数(几格),speed:50多少ms移动一次}
function mover(moverConfig){
var nowIn = 0;//当前抽奖效果在哪
var removeNum = 9;//当前需要删除的效果
var timer = setInterval(()=>{
if(nowIn!=0){//如果现在不是第一个
// 那么需要删除的效果就是当前效果的前一个
removeNum = nowIn-1;
}
// 删除和添加效果代码
_domArr[removeNum].setAttribute('class','item');
_domArr[nowIn].setAttribute('class','item item-on');
// 每次操作完后效果所属index加一
nowIn++;
if(nowIn == moverConfig.moveTime){//当移动次数等于需要移动次数
clearInterval(timer);//停止
if(moverConfig.moveTime == 10){//如果还在前4圈
observeOb.fire('finish')
}
}
},moverConfig.speed)
}
// 动画控制模块
function moveControll(){
var final = getFinal();
var _circle = Math.floor(final/10,0);//拿到一共跑的圈数
var stopNum = final%10;//最终停留
var _speed = 2000;//初始化速度
var _runCircle = 0;//已经跑的圈数
// 初始化开始运动
mover({
moveTime:10,speed:_speed
})
//注册监听
observeOb.regist('finish',function(){
var _time = 0;
// 每次完成就减慢50ms
_speed -= 50;
// 完成圈数+1
_runCircle ++;
// 跑的小于等于总圈数
if(_runCircle<=_circle){
// 还没跑完,就每次还得跑十次
_time = 10;
}else{
// 跑完就等于最终停留
_time = stopNum;
}
mover({
moveTime:_time,
speed:_speed
})
})
}
htmlInit(document.getElementById('app'));
moveControll();
ps:行为型
目的:为了避免请求发送者与多个请求处理者耦合在一起,形成一个链条,
应用场景:把操作分割成一系列模块,每个模块只处理自己的事情,
一:基本结构(代码模式不是固定的,需要记住的是思维)
function m1(){
}
function m2(){
}
function m3(){
}
_result = m1(_result);
_result = m2(_result);
_result = m3(_result);
ps:把要做的事情组织成一条有序的链条,通过在这条链条传递消息来完成功能.适用于不涉及到复杂异步的操作
二:示例
例子:Axios请求拦截器
需求:axios拦截器的设计,可以看出是一个职责链思想去处理请求
// axios拦截器
// 请求拦截器 => 请求 => 响应拦截器
//首先有axios方法
function axios(){
//拦截器
this.interceptors = {
request:new interceptorsManner(),
response:new interceptorsManner()
}
}
//定义请求部分,假设为request
axios.prototype.request = function(){
//dispathRequest这个方法才是真正的发请求出去,这里不写了,知道就好
var chain = [dispathRequest,undefined];
// 拿到状态promise
var promise = Promise.resolve(config);
// 在使用拦截时候,拿到请求拦截添加到数组前面中去,依次调用
this.interceptors.request.handlers.forEach((interceptors)=>{
chain.unshift(interceptors.fulfilled,interceptors.rejected);
})
// 响应拦截放后面
this.interceptors.response.handlers.forEach((interceptors)=>{
chain.push(interceptors.fulfilled,interceptors.rejected);
})
while(chain.length){//循环,每次去拿出成功的和失败的执行
promise = promise.then(chain.shift(),chain.shift())
}
}
//拦截器类
function interceptorsManner(){
this.handlers = [];
}
// use的时候往handlers中添加请求拦截和响应拦截
interceptorsManner.prototype.use = function(fulfilled,rejected){
this.handlers.push({
fulfilled:fulfilled,
rejected:rejected
})
}
例子:利用职责链组织一个表单验证
需求:有一个表单,需要先前端验证,再后端验证
// 表单事件绑定=>前端验证=>后端验证
input.onblur = function(){
// 我们简单定义一下:假设input.value拿到value值(实际上不可能)
var _value = input.value;
var _arr = [font,back];
// 定义一个同步函数
async function test(){
// 初始化值就等于value
var _result = value;
while(_arr.length>0){//当链中还有时候
_result = await _arr.shift()(_result);
}
return _result;
}
// async拿到的是一个pomise
text().then((res)=>{
console.log(res)
})
}
// 前端验证 同步
function font(result){
}
// 后端验证 异步
function back(){
}
ps:要做的事情拆分成模块,模块之间只做自己模块的事情,然后传递结果
ps:技巧型
目的:解耦数据结构与数据操作,
应用场景:数据结构不希望与操作关联,适用于操作对象或者操作数据可能会变,操作对象不必要知道操作数据,相反同理
一:基本结构
// 我们要操作的data
var data=[];
var handler = function (){
}
handler.prototype.get = function(){
}
var vistor = function(handler,data){
handler.get(data);
}
ps:定义一个访问者,代替直接访问对象,来减少两个对象之间的耦合,
二:示例
例子:不同角色访问数据
需求:假设有一个公司的财务报表 财务关心支出收入 老板关系盈利,
// 报表
function report(){
this.income = '';//收入
this.cost = '';//支出
this.profit = '';//利润
}
function boss(){
}
boss.prototype.get = function(data){
}
function account(){
}
account.prototype.get = function(num1,num2){
}
// 设置一个访问者
function vistor(data,man){
var handle = {
boss:function(data){
man.get(data.profit);
},
account:function(data){
account.get(data.income,data.cost)
},
}
// 拿到类的原名字
handle[man.constructor.name](data);
}
vistor(new report(),new boss());
例子:表格操作
需求:一个可以新增,删除的表格,
function table(){
}
table.prototype.show = function(){
}
table.prototype.delete = function(){
vistor(this,tableDat,'delete',id)
}
table.prototype.add = function(){
}
var tableData = [
{
id:1,
name:'xxx',
price:'xxx'
}
]
function vistor(table,data,handle){
var handleOb = {
delete:function(id){
},
add:function(id,name,price){
},
}
// arguments是类数组,先变成真正的数组
var arg = Array.prototype.splice(arguments);
// 先把前三项,也就是table,data,handle删除掉,剩下的就是你写入的参数了
arg.splice(0,3);
handleOb[handle].apply(this,arg);
}