javascript-簡單理解設(shè)計(jì)模式

JS設(shè)計(jì)模式有很多種

在這里插入圖片描述

常見的設(shè)計(jì)模式

(1): 單例模式(Singleton Pattern)-創(chuàng)建

1: 第一種

// 單例模式(Singleton Pattern),后端用的比較多
// 單例模式創(chuàng)建多少對象就只有一個實(shí)例

var SinglePattern = (function(){
   function Single(name) {
     this.name = name;
   }
   return function(name) {
     if(!Single.instance) {
       Single.instance = new Single(name)
     }
     return Single.instance
   }
})()
var obj1 = new SinglePattern("張三")
var obj2 = new SinglePattern("李四")

2:第二種

var SinglePattern = (function(){
  var instance = null;
   function Single(name) {
     this.name = name;
   }
   return function(name) {
     if(!instance) {
      instance = new Single(name)
     }
     return instance
   }
})()
var obj1 = new SinglePattern("張三")
var obj2 = new SinglePattern("李四")

console.log("obj1========",obj1);
console.log("obj2========",obj2);
console.log(obj1 === obj2);

在這里插入圖片描述

應(yīng)用場景:數(shù)據(jù)一致性

(2): 工廠模式(Factory Pattern)- 創(chuàng)建

1: 第一種

function School(type) {
  var instance = new Object();
  instance.name = type.name;
  instance.age = type.age;
  instance.gender = type.gender;
  return instance;
}
var student = School({name: '學(xué)生',age: 10,gender: '男'})
var teacher = School({name: '老師',age: 23,gender: '女'})
console.log("student",student);
console.log("teacher",teacher);

在這里插入圖片描述

2:第二種

// 歷史
function History(d) { return this.data = d };
// 數(shù)學(xué)
function Math(d) { return this.data = d };

// 課程工廠

function FactoryPattern(type) {
  var Clazz = null;
  var data = null;
  switch(type) {
    case "history":
      Clazz = History;
      data = "歷史"
      break;
    case "math":
      Clazz = Math;
      data = '數(shù)學(xué)';
      break;
  }
  return new Clazz(data)
}

var c1 = FactoryPattern("history")
var c2 = FactoryPattern("math")
console.log(c1,c2);

在這里插入圖片描述

工廠模式應(yīng)用場景:工廠模式-創(chuàng)建(批量生產(chǎn)對象)

(3): 觀察者模式(Observer Pattern)-行為

1: ES5寫法

// 依賴收集器
function Dep() {
  this.subs = [] // 數(shù)據(jù)存儲,存儲需要訂閱的內(nèi)容,未來方便通知
}
Dep.prototype.addSub = function(sub) {
  this.subs.push(sub)
}
// 事件觸發(fā)方:通知的行為
Dep.prototype.notify = function() {
  this.subs.forEach(sub => {
    sub.update()
  });
}

// 觀察者模式
function Observer(name) {
  this.name = name;
}
Observer.prototype.update = function() {
  console.log(this.name,"檢測到了更新");
}


var obj1 = new Observer("小a")
var obj2 = new Observer("小b")

// 創(chuàng)建主題對象,對主題產(chǎn)生訂閱
var dep = new Dep()
dep.addSub(obj1)
dep.addSub(obj2)
dep.notify()
在這里插入圖片描述

2: ES6寫法

// 觀察者模式
class Observer {
  constructor(name) {
    this.name = name;
  }
  update() {
    console.log(this.name + "更新了")
  }
}
// 依賴收集器
class Dep {
  constructor() {
    this.subs = []
  }
  addSubs(watcher) {
    this.subs.push(watcher)
  }
  notify() {
    this.subs.forEach(w => {
      w.update()
    })
  }
}
var obj1 = new Observer("小a")
var obj2 = new Observer("小b")
var dep = new Dep()
dep.addSubs(obj1)
dep.addSubs(obj2)
dep.notify()

在這里插入圖片描述

Observer 負(fù)責(zé)視圖更新,Dep負(fù)責(zé)收集依賴和通知,視圖和邏輯分離,具備消息訂閱功能
應(yīng)用場景: 消息訂閱

附加:手寫一個eventsBus

class Bus {
  constructor(){
    this.handlers = {}
  }
  // 實(shí)現(xiàn)事件訂閱on
  on(name,callback) {
    // 沒有就新增
    if(!this.handlers[name]) {
      this.handlers[name] = []
    }
    // 有的話就進(jìn)行push
    this.handlers[name].push(callback)
  }
  // 實(shí)現(xiàn)事件發(fā)布emit
  emit(name,...args) {
    if(this.handlers[name]) {
      this.handlers[name].forEach(cb=>{
        cb(...args)
      })
    }
  }
  // 實(shí)現(xiàn)事件的銷毀off
  off(name,callback) {
    const callbacks = this.handlers[name]
    const index = callbacks.indexOf(callback)
    if(index!=-1) {
      callbacks.splice(index,1)
      delete this.handlers[name]
    }
    console.log("this.handlers",this.handlers);
  }
}

var $bus = new Bus();
var fn1 = function(e) {
  console.log("e",e);
 }
var fn2 = function(e) {
  console.log("e",e);
 }
$bus.on("sendMessage",fn1)
$bus.emit("sendMessage",{id:1,name:"張三"})
$bus.off("sendMessage",fn1)


$bus.on("sendInfo",fn2)
$bus.emit("sendInfo","你好呀!")
$bus.off("sendInfo",fn2)


在這里插入圖片描述

(4) 裝飾器模式(Decorator Pattern)-結(jié)構(gòu)

// Decorator Pattern 裝飾器模式

function Coffee() {
  this.info = "苦咖啡";
}
Coffee.prototype.show = function() {
  console.log(this.info); // 這個this就是構(gòu)造函數(shù)本身
}

// 裝飾器 
function CoffeeDecorator(coffee) {
  this.coffee = coffee; // 保存被裝飾的對象
}
// 加工外部操作,更加利于加工類的擴(kuò)展和維護(hù)
CoffeeDecorator.prototype.show = function() {
  console.log("糖是甜的呦!");
  this.coffee.show()
}
var co1 = new Coffee();
var coDc = new CoffeeDecorator(co1)
coDc.show()
在這里插入圖片描述
(5) 代理模式(Proxy-Pattern)
// 創(chuàng)建代理類

function Coffee() {
  this.info = "苦咖啡"; // 代碼不利于維護(hù)
}
Coffee.prototype.show = function() {
  console.log(this.info);
}

// 創(chuàng)建代理類
function CoffeeProxy() {
  // 用代理替換文本 不存在本體
  this.coffee = new Coffee()
}
CoffeeProxy.prototype.show = function() {
  console.log("代理的甜");
  this.coffee.show()
}
var cp1 = new CoffeeProxy()
cp1.show()

其實(shí)裝飾器模式和代理模式很相似,如何選擇呢,根據(jù)當(dāng)前是否有裝飾著實(shí)例,如果沒有,就用代理模式創(chuàng)建一個實(shí)例

(6): 策略模式(Strategy-Pattern)-行為

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>

  <body>
    <form action="" id="form">
      <input type="text" id="username" placeholder="用戶名" /><br />
      <input type="text" id="password1" placeholder="密碼" /><br />
      <input type="text" id="password2" placeholder="確認(rèn)密碼" /><br />
      <input type="text" id="phone" placeholder="電話" /><br />
      <button>提交</button>
    </form>
    <script>
      function Validate() {}
      // 四個功能函數(shù), 通過外部 調(diào)用rules[名稱]獲取
      Validate.prototype.rules = {
        // 是否手機(jī)號
        isMobile: function (str) {
          var rule = /^1[3,4,5,7,8,9][0-9]\d{8}$/;
          return rule.test(str);
        },
        // 是否必填
        isRequired: function (str) {
          // 除去首尾空格
          var value = str.replace(/(^\s*)|(\s*$)/g, "");
          return value !== "";
        },
        // 最小長度
        minLength: function (str, length) {
          var strLength = str.length;
          return strLength >= length;
        },
        // 是否相等
        isEqual: function () {
          // 可以接收多個參數(shù)比較
          var args = Array.prototype.slice.call(arguments);
          // 取首項(xiàng)與后面所有的項(xiàng)比較,如果每個都相等,就返回true
          var equal = args.every(function (value) {
            return value === args[0];
          });
          return equal;
        },
      };
      Validate.prototype.test = function (rules) {
        var v = this;
        var valid; // 保存校驗(yàn)結(jié)果
        // 外部傳遞的規(guī)則配置參數(shù)rules
        for (var key in rules) {
          // 遍歷校驗(yàn)規(guī)則對象
          // 數(shù)組
          for (var i = 0; i < rules[key].length; i++) {
            // 遍歷每一個字段的校驗(yàn)規(guī)則
            var ruleName = rules[key][i].rule; // 獲取每一個校驗(yàn)規(guī)則的規(guī)則名
            var value = rules[key][i].value; // 獲取每一個校驗(yàn)規(guī)則的校驗(yàn)值
            if (!Array.isArray(value)) {
              // 統(tǒng)一校驗(yàn)值為數(shù)組類型
              value = new Array(value);
            }
            // ruleName         // 確保this是validate對象
            // [this.password2.value, 6]  || this.password2.value
            var result = v.rules[ruleName].apply(v, value); // 調(diào)用校驗(yàn)規(guī)則方法進(jìn)行校驗(yàn)
            if (!result) {
              // 如果校驗(yàn)不通過,就獲取校驗(yàn)結(jié)果信息,并立即跳出循環(huán)不再執(zhí)行,節(jié)約消耗
              valid = {
                errValue: key,
                errMsg: rules[key][i].message,
              };
              break;
            }
          }
          if (valid) {
            // 如果有了校驗(yàn)結(jié)果,代表存在不通過的字段,則立即停止循環(huán),節(jié)約消耗
            break;
          }
        }
        return valid; // 把校驗(yàn)結(jié)果反悔出去
      };

      var formData = document.getElementById("form");
      formData.onsubmit = function () {
        event.preventDefault();
        var validator = new Validate();
        // 驗(yàn)證規(guī)則 抽離出來, 便于更為靈活的應(yīng)用驗(yàn)證器
        // 【使用參數(shù)傳遞:便于擴(kuò)展】 配置上更加語義化
        //  將規(guī)則作為規(guī)則名稱封裝,【提高了復(fù)用性】
        //  驗(yàn)證器已經(jīng)和DOM解耦,只關(guān)心規(guī)則和數(shù)據(jù)以及message
        var result = validator.test({
          // 和id一致才能獲取
          username: [
            {
              rule: "isRequired",
              value: this.username.value,
              message: "用戶名不能為空!",
            },
          ],
          password1: [
            {
              rule: "isRequired",
              value: this.password1.value,
              message: "密碼不能為空!",
            },
            {
              rule: "minLength",
              value: [this.password1.value, 6],
              message: "密碼長度不能小于6個字符!",
            },
          ],
          password2: [
            {
              rule: "isRequired",
              value: this.password2.value,
              message: "確認(rèn)密碼不能為空!",
            },
            {
              rule: "minLength",
              value: [this.password2.value, 6],
              message: "確認(rèn)密碼長度不能小于6個字符!",
            },
            {
              rule: "isEqual",
              value: [this.password2.value, this.password1.value],
              message: "確認(rèn)密碼與原密碼不相同!",
            },
          ],
          isMobile: [
            {
              rule: "isRequired",
              value: this.phone.value,
              message: "手機(jī)號不能為空!",
            },
            {
              rule: "isMobile",
              value: this.phone.value,
              message: "手機(jī)號格式不正確!",
            },
          ],
        });
        if (result) {
          console.log(result);
        } else {
          console.log("校驗(yàn)通過");
        }
      };
    </script>
  </body>
</html>

策略模式常用于代碼的復(fù)用性和可擴(kuò)展性

在這里插入圖片描述

(7): 適配器模式(Adaptor-Pattern)-結(jié)構(gòu)

這個我們平常很常見


var xiaomi = {
  view() {
    console.log("顯示小米屏幕");
  }
}
var iphone = {
  view() {
    console.log("顯示iphone屏幕");
  }
}
// 保證手機(jī)的高內(nèi)聚,所以我們吧后續(xù)的功能加到外邊
// 調(diào)用手機(jī),view的功能封裝起來
function use(type) {
  if(type === 'xiaomi'){
    xiaomi.view()
  } else if(type === 'iphone') {
    iphone.view()
  }
}
use("xiaomi")

適配器模式通常用于做代碼的兼容

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容