近日,重新学习梳理了JS设计模式,特地在这里做下笔记记录下。

迭代器模式

特征

  • 顺序访问集合
  • 使用者无需知道内部集合结构

代码

class Iterator {
  constructor(container) {
    this.list = container.list;
    this.index = 0;
  }
  next() {
    if (this.hasNext()) {
      return this.list[this.index++];
    }
    return null;
  }
  hasNext() {
    if (this.index >= this.list.length) {
      return false;
    }
    return true;
  }
}
class Container {
  constructor(list) {
    this.list = list;
  }
  // 生成遍历器
  getIterator() {
    return new Iterator(this);
  }
}
let container = new Container([1, 2, 3, 4, 5]);
let iterator = container.getIterator();

while (iterator.hasNext()) {
  console.log(iterator.next());
}

应用场景

1、ES6 iterator

  • ES6 语法中,有序集合的数据类型已经很多
    Array、Map、Set、String、TypedArray、arguments、NodeLists
  • 需要一个统一的遍历接口来遍历所有的数据类型
    (注意,object 不是有序集合,可以用 Map 替代)
  • 以上数据类型,都有[Symbol.iterator]属性
  • 属性值是函数,执行函数返回一个迭代器
  • 这个迭代器有 next 方法可顺序迭代子元素
  • 可运行 Array.prototype[Symbol.iterator]来测试
function each(data) {
  // 生成遍历器
  let iterator = data[Symbol.iterator]();

  // console.log(iterator.next());  有数据返回{value:1,done:false}
  // console.log(iterator.next());  没有数据返回{value:undefined,done:true}

  let item = { done: false };
  while (!item.done) {
    item = iterator.next();
    if (!item.done) {
      console.log(item.value);
    }
  }
}

// 测试代码
let arr = [1, 2, 3];
let nodeList = document.getElementByTagName("p");
let m = new Map();
m.set("a", 100);
m.set("b", 200);

each(arr);
each(nodeList);
each(m);

// 'Symbol.iterator' 并不知道人人都知道
// 也不是每个人都需要封装个each方法
// 因此有了 'for...of' 语法
function each(data) {
  // data 要有symbol.iterator 属性
  for (let item of data) {
    console.log(item);
  }
}

each(arr);
each(nodeList);
each(m);

状态模式

特点

  • 一个对象有状态变化
  • 每次状态变化都会触发一个逻辑
  • 不能总是用 if...else...来控制
  • 主体里面可以获取状态的信息,状态的切换和状态的获取 是分开的

代码

// 状态(红灯、黄灯、绿灯)
class State {
  constructor(color) {
    this.color = color;
  }

  handle(context) {
    console.log(`turn to ${this.color} light`);
    context.setState(this);
  }
}
// 主体
class Context {
  constructor() {
    this.state = null;
  }
  getState() {
    return this.state;
  }
  setState(state) {
    this.state = state;
  }
}

//test
let context = new Context();

let green = new State("green");
let red = new State("red");
let yellow = new State("yellow");

green.handle(context);
context.getState();

red.handle(context);
context.getState();

yellow.handle(context);
context.getState();

应用场景

1、手写一个 promise

// 状态机模型
import StateMachine from "javascript-state-machine";

let fsm = new StateMachine({
  init: "pending",
  transitions: [
    {
      name: "resolve",
      from: "pending",
      to: "fullfilled"
    },
    {
      name: "reject",
      from: "pending",
      to: "rejected"
    }
  ],
  methods: {
    // 监听resolve
    onResolve: function(state, data) {
      // state--当前状态机实例;data--fsm.resolve(xxx)传递的参数
      data.successList.forEach(fn => fn());
    },
    // 监听reject
    onReject: function(state, data) {
      // state--当前状态机实例;data--fsm.resolve(xxx)传递的参数
      data.failList.forEach(fn => fn());
    }
  }
});
// 定义一个Promise
class MyPromise {
  constructor(fn) {
    this.successList = [];
    this.failList = [];
    fn(
      function() {
        // resolve函数
        fsm.resolve(this);
      },
      function() {
        // reject 函数
        fsm.reject(this);
      }
    );
  }

  then(successFn, failFn) {
    this.successList.push(successFn);
    this.failList.push(failFn);
  }
}

function loadImg() {
  const promise = new Promise(function(resolve, reject) {
    let img = document.createElement("img");
    img.onload = function() {
      resolve(img);
    };
    img.onerror = function() {
      reject();
    };
    img.src = src;
  });

  return promise;
}

let src = "https://www.baidu.com/img/a.png";
let result = loadImg(src);

result.then(
  function() {
    console.log("ok1");
  },
  function() {
    console.log("fail1");
  }
);

result.then(
  function() {
    console.log("ok2");
  },
  function() {
    console.log("fail2");
  }
);

原型模式

特点

  • 类似 clone,因为有的场景使用 new 不合适

应用场景

// 一个原型对象
const prototype = {
  getName: function() {
    return this.first + " " + this.last;
  },
  say: function() {
    alert("hello");
  }
};
// 基于原型对象 创建x
let x = Object.create(prototype);
x.first = "A";
x.last = "B";
alert(x.getName());
x.say();

// 基于原型对象 创建y
let y = Object.create(prototype);
y.first = "A";
y.last = "B";
alert(y.getName());
y.say();

桥接模式

特点

  • 用于把抽象化与实现化解耦
  • 使得二者可以独立变化

代码

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}`);
  }
}

// test
let red = new Color("red");
let yellow = new Color("yellow");
let circle = new Shape("circle", red);
circle.draw();

let triangle = new Shape("triangle", yellow);
triangle.draw();

组合模式

特点

  • 生成树形结构,表示“整体-部分”关系
  • 让整体和部分都具有一致的操作方式

应用场景

<div id="div1" class="container">
  <p>123</p>
  <p>456</p>
</div>

{
  tag:'div',
  attr:{
    id:"div1",
    class:"container"
  },
  children:[
    {
      tag:"p",
      attr:{},
      children:['123']
    },
    {
      tag:"p",
      attr:{},
      children:['456']
    }
  ]
}

享元模式

特点

  • 共享内存(主要考虑内存,而非效率)
  • 相同的数据 共享使用

应用场景

JS 没有太经典案例

策略模式

特点

  • 不同策略分开处理
  • 避免出现大量 if...else 或者 switch...case...
  • 个人理解 就是使用对象来组件策略

应用场景

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 用户购买");
    }
  }
}

// 使用--策略模式
class MemberUser {
  buy() {
    console.log("会员用户购买");
  }
}

class VipUser {
  buy() {
    console.log("vip 购买");
  }
}

class OrdinaryUser {
  buy() {
    console.log("普通用户购买");
  }
}

let u1 = new OrdinaryUser();
u1.buy();

let u2 = new MemberUser();
u2.buy();

let u3 = new VipUser();
u3.buy();

模版方式模式

特点

  • 将一个 class 中的方法,以一定顺序封装到一个方法

应用场景

class Action {
  handle() {
    this.handle1();
    this.handle1();
    this.handle1();
  }

  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 Receive {
  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();
  }
}

//test
//士兵
let soldier = new Receiver();
// 小号手
let trumpeter = new Command(soldier);

// 将军
let general = new Invoker(trumpeter);

general.invoke();

应用场景

  • 网页富文本编辑器,浏览器封装了一个命令对象
  • document.execCommand('bold');
  • document.execCommand('unfo');

备忘录模式

特点

  • 随时记录一个对象的状态变化
  • 随时可以恢复之前的某个状态(如撤销功能)

代码

// 备忘录模式
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;
  }

  saveContentToMenento() {
    return new Memento(this.content);
  }

  getContentFromMemento() {
    this.content = memento.getContent();
  }
}

//test
let editor = new Editor();
let careTaker = new CareTaker();
editor.setContent("11");
editor.setContent("22");
careTaker.add(editor.saveContentToMenento()); // 存储备忘录
editor.setContent("33");
careTaker.add(editor.saveContentToMenento()); // 存储备忘录
editor.setContent("44");

console.log(editor.getContent());
editor.getContentFromMenento(careTaker.get(1));

console.log(editor.getContent());
editor.getContentFromMenento(careTaker.get(0));
console.log(editor.getContent());

前端熟练工
1.8k 声望66 粉丝

要做前端架构师的正在前行的人