JS設計模式學習(2)

近日,從新學習梳理了JS設計模式,特意在這裏作下筆記記錄下。javascript

迭代器模式

特徵

  • 順序訪問集合
  • 使用者無需知道內部集合結構

代碼

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

應用場景

一、ES6 iteratorjava

  • 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();

應用場景

一、手寫一個 promisenode

// 狀態機模型
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());
相關文章
相關標籤/搜索