构造
通过构造函数,减少对象变量声明,同时能得到对象相同的结构
1 2 3 4 5 6 7 8 9 10
| function Info(name,age){ this.name = name; this.age = age; this.log = ()=>{ console.log(this.name,this.age) } }
const p = new Info('xiaoming',22); const p2 = new Info('xiaohong',33)
|
原型模式
构造模式有个大的问题,log函数每次new Info时,都会声明一次,会在内存中重新分配 一块内存
实际上,log做的是同一个事,所以通过原型模式可以弥补这面的不足
这样,每一个new Info时,log函数只声明了一次
1 2 3 4 5 6 7 8 9 10 11
| function Info(name,age){ this.name = name; this.age = age; }
Info.prototype.log = ()=>{ console.log(this.name,this.age) }
const p = new Info('xiaoming',22); const p2 = new Info('xiaohong',33)
|
类模式
原型模式可以用ES6方式简写,实现是一样的
1 2 3 4 5 6 7 8 9
| class Info{ constructor(name,age){ this.name = name; this.age = age; } log(){ console.log(this.name,this.age) } }
|
通过class实例的类,方法挂载到了prototype上
工厂模式
利用函数、类处理多个重复的逻辑,返回一个对象,开发者无需关心内部调用步骤
1 2 3 4 5 6 7 8 9
| function stu(name,age){ const opt = {}; opt.name = name; opt.age = age; return opt; }
stu('xiaoming',22)
|
建造者模式
与工厂模式
不同,工厂模式由内部自己调用完成 最后输入对象。
建造者模式
让开发者了解全程调用过程,利用一个函数完成 整个步骤的调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class Mask{ constructor(opt){ this.defualtData = opt; } setData(data){ this.defaultData = data; } render(){ document.body.appendChild(template) } }
function buildFunc(newClass){ newClass.setData(); newClass.render(); }
buildFunc(new Mask());
|
单例设计模式
一个class类,new 多次,始终只有一个最终生成的实例,即new class() ===new class() ===new class();
单例设计模式,实现过程为,一个闭包,用一个变量记录是否已经 new实例过,如果是返回记录的实例。否则new一个实例并赋值给闭包变量返回。
ES5写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| const func = (()=>{ let install = null;
class Info{ constructor(name,age){ this.name = name; this.age = age; } log(){ console.log(this.name,this.age); } }
return function(name,age){ if(!install){ install = new Info(name,age); } return install; } })();
const p1= new func('xiaochen',25) const p2= new func('xiaoming',215)
|
ES6写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Info{ constructor(name,age){ if(!Info.install){ this.name = name; this.age = age; Info.install = this; } return Info.install; } log(){ console.log(this.name,this.age); } }
const p1 = new Info('xiaohong',22); const p2 = new Info('xiaoming',33); console.log(p1,p2,p1===p2);
|
弹窗案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| const onceMode = (()=>{ let install = null; return function(){ if(!install){ const mask = document.createElement('div'); mask.classList.add('el-dialog'); mask.style.cssText = "width: 200px;height: 200px;background-color: #ccc;display: none"; document.body.appendChild(mask); install = mask; } return install; } })();
function openMaskBtnBindEvent(){ const btn = document.querySelector('#openMask'); btn.addEventListener('click',()=>{ const maskDom = onceMode(); maskDom.style.display="block"; }) }
function clasMaskBtnBindEvent(){ const btn = document.querySelector('#closeMask'); btn.addEventListener('click',()=>{ const mask = document.querySelector('.el-dialog'); if(mask){ mask.style.display = "none" } }) } window.addEventListener('load',function(){ openMaskBtnBindEvent(); clasMaskBtnBindEvent(); })
|
装饰器模式
装饰器模式
主要为解决,函数扩展的问题,请看以下问题
1 2 3 4
| function test(){ console.log('111111') } test();
|
现在我们希望在test执行前输出”00000”,执行后输出 “22222”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| Function.prototype.before = function (beforeFunc) { let that = this; return function () { beforeFunc.apply(this, arguments); const _this = that.apply(this, arguments); return _this; } } Function.prototype.after = function (afterFunc) { let that = this; return function () { const _this = that.apply(this, arguments); afterFunc.apply(this, arguments); return _this; } } function test() { console.log('1111111111'); return 'test func' }
const func = test .before(() => { console.log('000000') }) .after(() => { console.log('22222') });
func();
|
适配器模式
需求: 某项目需要接入两个地图页面展示,百度地图 和 高德地图 ,两个员工 一个负责百度地图 开发,一个负责高德地图开发。代码如下
接入百度地图的员工
1 2 3 4 5 6 7 8 9 10 11
| class BaiduMap{ init(){ ...... } show(){ document.body.innerHTML = '......' } close(){ ............. } }
|
接入高德地图的员工
1 2 3 4 5 6 7 8 9 10 11
| class GaoDeMap{ start(){ ...... } display(){ document.body.innerHTML = '......' } remove(){ ............. } }
|
上面两位员工 分别有自己的个人习惯,命名方式都不能,两个一起接入时,会造成不停的判断,为了解决这个问题,适配器模式的思维很有用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class GaoDeMapAdapator extends GaoDeMap{ init(){ this.start(); } show(){ this.display(); } close(){ this.remove(); } }
document.querySelector('#btn').click=function(){ let map= null; if("需要用百度地图 "){ map = new BaiduMap(); }else{ map = new GaoDeMapAdapator(); } map.init(); map.show(); map.close() }
|
**案例2 **
老项目为jqeury搭建,请求使用的$.ajax方式,现在要求将jquery改为axios写法。
以前方式: 大量删除$.ajax,改写为axios
新思维:
1 2 3 4 5 6
| import axios from 'axios' var $ = { ajax: (params)=>{ axios.get().... } }
|
不修改$.ajax,直接在window上挂载一个变量,内部重写ajax
策略模式
抽离重复逻辑,通过定义对象引用,让代码更加简洁。
老项目
1 2 3 4 5 6 7 8 9 10 11
| function computedMoney(level, base){ if(level === 1){ return base*6 } if(level === 2){ return base*4 } if(level === 3){ return base * 2 } }
|
改进后
1 2 3 4 5 6 7 8
| const computedBase = { 1: base=> base*6, 2: base=> base*4, 3: base=> base*2, } function computedMoney(level, base){ return computedBase[level](base); }
|
代理模式Proxy
利用数据监听劫持,代理其它逻辑,vue3简单实现更新数据驱动视图
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| var obj = { name: '' }
window.proxy = new Proxy(obj,{ get(target,key){ return target[key] }, set(target,key,val){ const p = document.querySelector('#main'); p.innerText = val; target[key] = val; } })
|
观察者模式
观察者模式定义了一对多的依赖关系,通过目标对象的状态更新,通知所有依赖观察者对象自动更新.
实例
点击发布通知,消息推送到绿色的6个组件,不考虑react/vue的思想,原生实现这个问题。
通过观察者模式,可以直接由目标对象更新,让依赖观察者自动更新

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| class Main{ constructor(){ this.domList = []; } add(elClass){ this.domList.push(elClass); } update(msg){ this.domList.forEach(item=>item.update(msg)) } }
class Observe{ constructor(el){ this.dom = document.querySelector(el); } update(msg){ this.dom.innerHTML = msg; } }
const DomList = new Main(); DomList.add(new Observe('#bar1')); DomList.add(new Observe('#bar2')); DomList.add(new Observe('#bar3')); DomList.add(new Observe('#bar4')); DomList.add(new Observe('#bar5')); DomList.add(new Observe('#bar6'));
document.querySelector("#search button").onclick=function(){ const input = document.querySelector('#search input'); const inputValue = input.value; if(inputValue){ DomList.update(inputValue); }
}
|
发布与订阅模式
观察者模式不能对事件进行精细化管理,比如,我希望自己手写函数,通过Main类最后统一调度。为了解决这个问题,有了发布与订阅模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| const Main = { events: {}, add(type, func) { if (this.events[type]) { this.events[type].push(func); } else { this.events[type] = [func]; } }, del(type, func) { if (!func) { this.events[type] && (this.events[type].length = 0); return; } if (this.events[type]) { this.events[type] = this.events[type].filter(item => item !== func); } }, publish(type,data) { this.events[type].forEach(item => item(data)); } };
function updateInnerHTML({msg}){ document.querySelector('.container main').innerHTML = msg; }
function setStyle(){ document.querySelector('.container main').style.color="red"; }
window.onload=function(){ Main.add('updateInnerHTML', updateInnerHTML); const lis = document.querySelectorAll('.container li'); console.log(lis); lis.forEach(item=>{ item.onclick=function(){ const text = this.innerHTML; Main.publish('updateInnerHTML', { msg: text}); } }) }
|
模块模式
除了ES6模块化export imort语法,可以使用ES5类 闭包自执行函数s
1 2 3 4 5 6
| var Main = (function() { var name = '', age = 22; return function(){ return { name,age, func} } })();
|