JS設計模式學習(1)

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

工廠模式

特色

簡單一句話歸納 就是 return 一個 new 實例html

應用場景:

  • jQuery 建立
  • React.createElement();
  • vue.component();

單例模式

特色

  • 單例模式 用到了 Java 中的 private 特性

代碼

class SingleObject {
  login() {
    console.log("login...");
  }
}

SingleObject.getInstance = (function() {
  let instance;
  return function() {
    if (!instance) {
      instance = new SingleObject();
    }
    return instance;
  };
})();

let obj1 = SingleObject.getInstance();
obj1.login();

let obj2 = SingleObject.getInstance();
obj2.login();

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

console.log("分割線");

let obj3 = new SingleObject();
obj3.login();
console.log("obj1===obj3", obj1 === obj3);

應用場景

一、 jQuery $ 只有一個vue

if (window.jQuery !== null) {
    return window.jQuery;
  } else {
    //   進行初始化操做
  }

二、 登陸框java

class LoginForm {
      constructor() {
        this.state = "hide";
      }

      show() {
        if (this.state === "show") {
          alert("已經顯示");
          return;
        }
        this.state = "show";
        console.log("登陸框已經顯示");
      }

      hide() {
        if (this.state === "hide") {
          alert("已經隱藏");
          return;
        }
        this.state = "hide";
        console.log("登陸框已經隱藏");
      }
    }
    LoginForm.getInstance = (function() {
      let instance;
      return function() {
        if (!instance) {
          instance = new LoginForm();
        }
        return instance;
      };
    })();

    let login1 = LoginForm.getInstance();
    login1.show();

    let login2 = LoginForm.getInstance();
    login2.show();

    console.log("login1===login2", login1 === login2);

三、 vuex 和 redux 中的 store state 是單例模式node

適配器模式

特色

  • 舊接口格式 和 使用者不兼容
  • 中間加一個適配器轉換接口

代碼

class Adaptee {
  specificRequest() {
    return "德國標準插頭";
  }
}

class Target {
  constructor() {
    this.adaptee = new Adaptee();
  }

  request() {
    let info = this.adaptee.specificRequest();
    return `${info} - 轉換器 - 中國標準插頭`;
  }
}

let target = new Target();
let res = target.request();
console.log(res);

應用場景

一、 封裝舊接口react

// 本身封裝的ajax 以下
  ajax({
    url: "/getData",
    type: "POST",
    dataType: "json",
    data: {
      id: "123"
    }
  }).done(function() {});
  // 但由於歷史緣由,代碼中全是;
  // $.ajax({...})

  // 作一層適配器
  var $ = {
    ajax: function(option) {
      return ajax(option);
    }
  };

二、Vue computed 場景ajax

<div id="example">
  <p>Origin Message:{{message}}</p>
  <p>Computed Message:{{reversedMessage}}</p>
</div>
var vm = new Vue({
    el: "#example",
    data: {
      message: "Hello"
    },
    computed: {
      // 計算屬性的 getter
      reversedMessage: function() {
        // `this`指向vm實例
        return this.message
          .split("")
          .reverse()
          .join("");
      }
    }
  });

裝飾器模式

特色

  • 爲對象添加新功能
  • 不改變其原有的結構和功能
  • 裝飾器是一個函數

代碼

// 基礎代碼
@decorator
class A {}
// 等同於
class A {}
a = decorator(A) || A;
// 裝飾器傳參 加參數
function testDesc(isDec) {
  return function(target) {
    target.isDec = isDec;
  };
}

@testDesc(true)
class Demo {
  //...
}
alert(Demo.isDec);

應用場景

一、 ES7 裝飾器vuex

  • 配置環境-驗證裝飾器環境是否成功
@testDesc
class Demo {}

function testDesc(target) {
    target.isDec = true;
}
alert(Demo.isDec);
  • 能夠裝飾 class類
//一、mixin 實例
function mixins(...list) {
  return function(target) {
    Object.assign(target.prototype, ...list);
  };
}

const Foo = {
  foo() {
    alert("foo");
  }
};

@mixins(Foo)
class MyClass {}

let obj = new MyClass();
obj.foo();
  • 能夠裝飾 方法function
// 例 1
function readonly(target, name, descriptor) {
    //descriptor 是描述對象 (Object.defineProperty中會用到),原來的值以下
    //{
    //  value:specifiedFunction,
    //  enumerable:false,
    //  configurable:true,
    // writable:true
    //}
    descriptor.writable = false;
    return descriptor;
}
class Person {
    constructor() {
      this.first = "A";
      this.last = "B";
    }

    // 裝飾方法
    @readonly
    name() {
      return `${this.first} ${this.last}`;
    }
}
var p = new Person();
console.log(p.name());
//p.name=function(){} // 這裏會報錯,由於name 是隻讀屬性

二、例 2json

function log(target, name, descriptor) {
    var oldValue = descriptor.value;
    descriptor.value = function() {
      console.log(`Calling ${name} with`, arguments);
      return oldValue.apply(this, arguments);
    };
    return descriptor;
  }

class Math {
    //裝飾方法
    @log
    add(a, b) {
      return a + b;
    }
  }

const math = new Math();
const result = math.add(2, 4);
console.log("result", result);
  • core-decorators
    第三方類庫

代理模式

特色

  • 使用者無權訪問目標對象
  • 中間加代理,經過代理作受權和控制

代碼

class ReadImg {
  constructor(fileName) {
    this.fileName = fileName;
    this.loadFromDisk();
  }

  display() {
    console.log(`display...` + this.fileName);
  }

  loadFromDisk() {
    console.log(`load...` + this.fileName);
  }
}

class ProxyImg {
  constructor() {
    this.realImg = new ReadImg();
  }
  display() {
    this.realImg.display();
  }
}

let proxyImg = new ProxyImg("1.png");
proxyImg.display();

應用場景

一、javascript 點擊事件redux

<div id="div1">
  <a href="#">a1</a>
  <a href="#">a2</a>
  <a href="#">a3</a>
  <a href="#">a4</a>
</div>

<script>
  var div1 = document.getElementById("div1");
  div1.addEventListener("click", function(e) {
    var target = e.target;
    if (target.nodeName === "A") {
      alert(target.innerHTML);
    }
  });
</script>

二、明星經紀人

// 明星
let star = {
  name: "劉德華",
  age: 50,
  phone: "15987452635"
};

// 經紀人
let agent = new Proxy(star, {
  get: function(target, key) {
    if (key === "phone") {
      //返回經紀人本身手機號
      return "15911111111";
    }

    if (key === "price") {
      // 明星不報價,經紀人報價
      return 120000;
    }

    return target[key];
  },
  set: function(target, key, value) {
    if (key === "customPrice") {
      if (val < 100000) {
        // 最低10萬
        throw new Error("價格過低");
      } else {
        target[key] = value;
        return true;
      }
    }
  }
});
console.log(agent.name);
console.log(agent.age);
console.log(agent.phone);
console.log(agent.price);

agent.customPrice = 90000;
console.log("agent.customPrice", agent.customPrice);

代理模式 和 適配器模式 對比

  • 適配器模式:提供一個不一樣的接口(如不一樣版本的接口)
  • 代理模式:提供如出一轍的接口

代理模式 和 裝飾器模式 對比

  • 裝飾器模式:擴展原有功能,原有功能不變且可直接使用
  • 代理模式:顯示原有功能,可是通過限制或者閹割以後的

外觀模式

特色

  • 爲子系統中的一組接口提供了一個高層接口
  • 使用者使用這個高層接口

代碼

function bindEvent(ele, type, selector, fn) {
  if (fn === null) {
    fn = selector;
    selector = null;
  }
  //...
}
// 調用
bindEvent(elem, "click", "#div1", fn);
bindEvent(elem, "click", fn);

觀察者模式

特色

  • 發佈訂閱
  • 一對多
  • 被動接收,非主動

代碼

class Subject {
  constructor() {
    this.state = 0;
    this.observers = [];
  }

  getState() {
    return this.state;
  }

  setState(state) {
    this.state = state;
    this.notifyAllObservers();
  }

  notifyAllObservers() {
    this.observers.forEach(observer => {
      observer.update();
    });
  }

  attach(observer) {
    this.observer.push(observer);
  }
}
// 觀察者模式
class Observer {
  constructor(name, subject) {
    this.name = name;
    this.subject = subject;
    this.subject.attach(this);
  }
  update() {
    console.log(`${this.name} update state:${this.subject.getState()}`);
  }
}

// 測試
let s = new Subject();
let o1 = new Observer("o1", s);
let o2 = new Observer("o2", s);
let o3 = new Observer("o3", s);

s.setState(1);

應用場景

一、網頁綁定事件

<button id="btn1">btn</button>
<script>
  $("#btn1").click(function() {
    console.log("1");
  });
  $("#btn1").click(function() {
    console.log("2");
  });
  $("#btn1").click(function() {
    console.log("3");
  });
</script>

二、Promise

function loadImg() {
  var promise = new Promise(function(resolve, reject) {
    var img = document.createElement("img");
    img.onload = function() {
      resolve(img);
    };
    img.onerror = function() {
      reject("圖片加載失敗");
    };

    img.src = src;
  });

  return promise;
}

var src = "https://www.baidu.com/static/new.png";
var result = loadImg(src);
result
  .then(function(img) {
    console.log("width", img.width);
    return img;
  })
  .then(function(img) {
    console.log("height", img.height);
  });

三、jQuery callbacks

var callbacks = $.Callbacks();
callbacks.add(function(info) {
  console.log("fn1", info);
});

callbacks.add(function(info) {
  console.log("fn2", info);
});

callbacks.add(function(info) {
  console.log("fn3", info);
});

callbacks.fire("gogogo");
callbacks.fire("fire");

四、nodejs 自定義事件
(1) EventEmitter

const EventEmitter = require("events").EventEmitter;

const emitter1 = new EventEmitter();
emitter1.on("some", function() {
  // 監聽some
  console.log("some event is occured 1");
});

emitter1.on("some", function() {
  // 監聽some
  console.log("some event is occured 2");
});

emitter1.emit("some");

(2) 基於(1)的應用

const EventEmitter = require("events").EventEmitter;

// 繼承
class Dog extends EventEmitter {
  constructor(name) {
    super();
    this.name = name;
  }
}

let simon = new Dog("simon");
simon.on("bark", function() {
  console.log(this.name, " barked-1");
});
simon.on("bark", function() {
  console.log(this.name, " barked-2");
});
setInterval(function() {
  simon.emit("bark");
}, 1000);

(3) 基於(1)的應用

//Stream 用到了自定義事件
var fs = require("fs");
var readStream = fs.createReadStream("./data/file1.txt");

var length = 0;
readStream.on("data", function(chunk) {
  length += chunk.toString().length;
});

readSteam.on("end", function() {
  console.log(length);
});

(4) 基於(1)的應用

var readline = require("readline");
var fs = require("fs");

var rl = readline.createInterface({
  input: fs.createReadStream("./data/file1.txt")
});

var lineNum = 0;
rl.on("line", function(line) {
  lineNum++;
});
rl.on("close", function() {
  console.log("lineNum", lineNum);
});

(5)nodejs 中:處理 http 請求;多進程通信

function serverCallback(req, res) {
  var method = req.method.toLowerCase(); // 獲取請求的方法
  if (method === "get") {
    // 省略三行,上文示例代碼中處理GET請求的代碼
  }
  if (method === "post") {
    // 接收post請求的內容
    var data = "";
    req.on("data", function(chunk) {
      // 一點點 接收內容
      data += chunk.toString();
    });

    req.on("end", function() {
      //
      res.writeHead(200, { "Content-Type": "text/html" });
      res.write(data);
      res.end();
    });
  }
}
//parent.js
var cp = require("child-process");
var n = cp.for("./sub.js");

n.on("message", function(m) {
  console.log("Parent got message: " + m);
});

n.send({ hello: "world" });

//sub.js
process.on("message", function(m) {
  console.log("child got message: " + m);
});

process.send({ foo: "bar" });

(6)vue 和 react 組件生命週期觸發(7)vue watch

相關文章
相關標籤/搜索