- Published on
JavaScript设计模式(下篇)
- Authors
- Name
- Hansuku
迭代器模式
迭代器模式意在,js 中遍历方法众多,foreach、for、for...of 等等针对的是不一样的数据格式,而迭代器模式的思想就是聚合这些遍历方法,我不需要知道我现在的数据是个什么样的格式,只要知道是要遍历的就可以了。 JavaScript 共有四种数据集合:数组 Array、对象 Object、Map、Set。后两个是 ES6 添加的
/*
假设遍历以下dom
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p> */
var arr = [1,2,3,4,5]
var nodeList = document.getElementsByTagName('a')
var $p = $p('p')
//遍历数组
arr.forEach(function(item){
console.log(item)
})
//遍历nodeList
var i,length = nodeList.length
for(i = 0;i < length;i++){
console.log(nodeList[i])
}
//遍历$a
$a.each(function(key,elem){
console.log(key,elem)
})
像上面这种代码,不同的数据格式调用不同的遍历方法,下面来整合在一起
/*
假设遍历以下dom
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p> */
var arr = [1,2,3,4,5]
var nodeList = document.getElementsByTagName('a')
var $p = $p('p')
function each(data){
var $data = $(data) //生成迭代器
$data.each(function(key,val){
console.log(key,val)
})
each(arr)
each(nodeList)
each($p)
}
class Container{
constructor(list){
this.list = list
}
getIterator(){
return new Iterator(this)
}
}
class Iterator{
constructor(container){
this.list = container.list
this.index = 0
}
nex(){
if(this.hasNext()){
return this.list[this.index++]
}
return null
}
hasNext(){
if(this.index >= this.list.length){
return false
}
return true
}
}
let arr = [1,2,3,4,5,6]
let container = new Container(arr)
let iterator = container.getIterator()
while(iterator,hasNext()){
console.log(iterator.next())
}
是否支持
iterator
的数据集合可以通过Symbol.interator
查询
function each(data){
let iterator = data[Symbol.iterator]()
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
}
let arr = [1,2,3,4]
let nodeList = document.getElementsByTagName('p')
let m = new Map()
m.set('a',100)
m.set('b',100)
each(arr)
// each(nodeList)
// each(m)
结合上面的代码 当我们不知道 data 是什么数据集合时 我们通过 while 来循环
function each(data){
let iterator = data[Symbol.iterator]()
let item = {done: false}
while(!item.done){
item = iterator.next()
if(!item.done){
console.log(item.value)
}
}
}
let arr = [1,2,3,4]
let nodeList = document.getElementsByTagName('p')
let m = new Map()
m.set('a',100)
m.set('b',100)
each(arr)
each(nodeList)
each(m)
ES6 中的
for...of
就是一个iterator
function each(data){
//带有遍历器特性的对象 : data[Symbol.iterator] 有值
for(let item of data){
console.log(data)
}
}
let arr = [1,2,3,4]
let nodeList = document.getElementsByTagName('p')
let m = new Map()
m.set('a',100)
m.set('b',100)
each(arr)
each(nodeList)
each(m)
状态模式
class State{
constructor(color){
this.color = color
}
handle(context){
console.log(`trun to ${this.color} ligth`)
context.setState(this)
}
}
class Context{
constructor(){
this.state = null
}
getState(){
return this.state
}
setState(state){
this.state = state
}
}
let context = new Context()
let green = new State('greem')
let yellow = new State('yellow')
let red = new State('red')
//绿灯亮了
green.handle(context)
console.log(context.getState())
//黄灯亮了
yellow.handle(context)
console.log(context.getState())
//红灯亮了
red.handle(context)
console.log(context.getState())
状态模式中有一个场景是有限状态机,JavaScript-state-machine 是一个有限状态机的第三方库
有限状态机实现收藏和取消收藏
// npm install --save javascript-state-machine
import StateMachine from 'javascript-state-machine'
//初始化状态机模型
let fsm = new StateMachine({
init:'收藏',
transitions:[
{
name: 'doStore',
from: '收藏',
to: '取消收藏'
},
{
name: 'deleteStore',
from: '取消收藏',
to: '收藏'
}
],
methods:{
// 监听执行收藏
onDoStore:function(){
alert('收藏成功')
updateText()
},
// 监听取消收藏
onDeleteStore:function(){
alert('取消收藏成功')
updateText()
}
}
})
//<button id="btn1"></button>
let btn = document.getElementById('btn1')
console.log(btn)
//按钮点击事件
btn.addEventListener('click',function(){
if(fsm.is('收藏')){
fsm.doStore()
}else{
fsm.deleteStore()
}
},false)
//更新按钮文案
function updateText(){
btn.innerText = fsm.state
}
//初始化文案
updateText()
Promise 有限状态机
- Promise 三种状态:pending fullfilled rejected
- pending -> fullfilled 或者 pending -> rejected
- 不可逆向变化
其他非典型 JavaScript 设计模式
行为型 |
---|
策略模式 |
模板方法模式 |
职责链模式 |
命令模式 |
备忘录模式 |
中介者模式 |
访问者模式 |
解释器模式 |
原型模式
clone 自己,生成一个新对象 Js 中的应用-
Object.create
//一个原型对象
let prototype = {
getName:function(){
return this.first + ' ' + this.last
},
say:function(){
alert('hello')
}
}
//创建原型x
let x = Object.create(prototype)
x.first = 'A'
x.last = 'B'
console.log(x.getName())
x.say()
//创建原型y
let y = Object.create(prototype)
y.first = 'C'
y.last= 'D'
console.log(y.getName())
y.say()
prototype 是 ES6 Class 的一种底层原理 class 是面向对象的基础,并不服务与某个模式
桥接模式
class Color{
constructor(name){
this.name = name
}
}
class Shape{
constructor(name ,color){
this.name = name
this.color = color
}
draw(){
console.log(`${this.color.name} ${this.name}`)
}
}
let red = new Color('red')
let yellow = new Color('yellow')
let circle = new Shape('circle',red)
circle.draw() //red circle
let triangle = new Shape('triangle', yellow)
triangle.draw() //yellow triangle
组合模式
生成树形结构,表示“整体-部分”关系 整体和单个节点的操作是一致的 整体和单个节点的数据结构也保持一致
// dom
// <div id="div1" class="container">
// <p>123</p>
// <p>456</p>
// </div>
//vnode
{
tag: 'div',
attr: {
id: 'div1',
className: 'container',VC },
children: [
{
tag: 'p',
attr:{},
children:['123']
},
{
tag:'p',
attr:{},
children:['456']
}
]
}
享元模式
共享内存(主要考虑内存,而非效率)
// 如果都绑定到a标签 对内存开销太大
// <div id="div1">
// <a href="#">a1</a>
// <a href="#">a2</a>
// <a href="#">a3</a>
// <a href="#">a4</a>
// <!-- 无限下拉列表 -- >
// </div>
var div1 = document.getElementById('div1')
div1.addEventListener('click',function(e){
var target = e.target
if(e.nodeName === 'A'){
alert(EventTarget.innerHtml)
}
})
策略模式
不同策略分开处理 避免出现大量
if...else
或者switch...case
js 中无经典场景
源代码:
class User{
constructor(type){
this.type = type
}
buy(){
if(this.type === 'ordinary'){
console.log('普通用户购买')
}else if(this.type === 'member'){
console.log('会员购买')
}else if(this.type === 'vip'){
console.log('VIP用户购买')
}
}
}
var u1 = new User('ordinary')
u1.buy()
var u2 = new User('member')
u2.buy()
var u3 = new User('vip')
u3.buy()
策略模式:
class OrdianryUser{
buy(){
console.log('普通用户购买')
}
}
class MemberUser{
buy(){
console.log('普通用户购买')
}
}
class VipUser{
buy(){
console.log('vip用户购买')
}
}
var u1 = new OrdianryUser()
u1.buy()
var u2 = new MemberUser()
u2.buy()
var u3 = new VipUser()
u3.buy()
模板方法模式
class Action{
handle(){
handle1()
handle2()
handle3()
}
handle1(){
console.log('1')
}
handle2(){
console.log('2')
}
handle3(){
console.log('3')
}
}
职责链模式
一步操作可能分为多个职责角色来完成 把这些角色都分开,然后用一个链串起来 将发起者和各个角色的处理者隔离
class Action{
constructor(name){
this.name = name
this.nextAction = null
}
setNextAction(action){
this.nextAction = action
}
handle(){
console.log(`¥{this.name}审批`)
if(this.nextAction != null){
this.nextAction.handle()
}
}
}
let a1 = new Action('组长')
let a2 = new Action('经理')
let a3 = new Action('总监')
a1.setNextAction(a2)
a2.setNextAction(a3)
a1.handle()
命令模式
执行命令时,发布者和执行者分开
class Receiver{
exec(){
console.log('执行')
}
}
class Command{
constructor(receiver){
this.receiver = receiver
}
cmd(){
console.log('触发命令')
this.receiver.exec()
}
}
class Invoker{
constructor(command){
this.command = command
}
invoke(){
console.log('开始')
this.command.cmd()
}
}
//士兵
let soldier = new Receiver()
//小号手
let trumpeter = new Command(soldier)
//将军
let general = new Invoker(trumpeter)
general.invoke()
JS 中的应用场景
网页富文本编辑器操作,浏览器封装了一个命令对象 document.execCommand('bold') document.execCommand('undo')
备忘录模式
随时记录一个对象的状态变化 随时可恢复之前的某个状态(撤销) 未找到 JS 中经典应用,除了一些工具(如编辑器)
//状态备忘
class Memento{
constructor(content){
this.content = content
}
getContent(){
return this.content
}
}
//备忘列表
class CareTaker{
constructor(){
this.list = []
}
add(memento){
this.list.push(memento)
}
get(index){
return this.list[index]
}
}
//编辑器
class Editor{
constructor(){
this.content = null
}
setContent(content){
this.content = content
}
getContent(){
return this.content
}
saveContentToMemento(){
return new Memento(this.cotent)
}
getContentFromMemento(memento){
this.content = memento.getContent()
}
}
//test
let edit = new Editor()
let careTaker = new CareTaker()
eidtor.setContent('111')
eidtor.setContent('222')
careTaker.add(editor.saveContentToMemento())//存储备忘录
editor.setContent('333')
careTaker.add(editor.saveContentToMemento())//存储备忘录
editor.setContent('444')
console.log(editor.getContent())
editor.getContentFromMemento(careTaker.get(1))//撤销
console.log(editor.getContent())
editor.getContentFromMemento(careTaker.get(0))//撤销
console.log(editor.getContent())
中介者模式
//买房子例子
//中介者
class Mediator{
constructor(a,b){
this.a = a
this.b = b
}
setA(){
let number = this.b.number
this.a.setNumber(number * 100)
}
setB(){
let number = this.a.number
this.b.setNumber(number / 100)
}
}
class A{
constructor(){
this.number = 0
}
setNumber(num,m){
this.number = num
if(m){
m.setB()
}
}
}
class B{
constructor(){
this.number = 0
}
setNumber(num,m){
this.number = num
if(m){
m.setA()
}
}
}
//test
let a = new A()
let b = new B()
let m = new Mediator(a,b)
a.setNumber(100,m)
console.log(a.number,b.number)
b.setNumber(100,m)
console.log(a.number,b.number)
访问者模式
将数据操作和数据结构进行分离 使用场景不多
解释器模式
描述语言语法如何定义,如何解释和编译 用于专业场景
例子-jQuery 模拟购物车
用到的模式:工厂模式 单例模式 装饰器模式 观察者模式 状态模式 模板方法模式 代理模式
UML 类图
源码地址
https://github.com/Hansuku/javascript-design-pattern
总结
- 工厂模式:$('xxx')创建商品
- 单例模式:购物车
- 装饰器模式:打点统计
- 观察者模式:网页事件,Promise
- 状态模式:添加到购物车 & 从购物车删除
- 模板方法模式:渲染有统一的方法,内部包含了各模块渲染
- 代理模式:打折商品信息处理