JavaScript的常見設計模式
阿新 • • 發佈:2021-01-27
一、設計模式:
是一套經過反覆使用、多人知曉的、經過分類的、程式碼設計經驗的總結。
優點:可以讓程式碼更容易被他人理解、保證程式碼的可靠性和可重用性。
W3shchol的設計模式分類:
構造器模式、模組化模式、暴露模組模式、單例模式、中介者模式、原型模式、命令模式、外觀模式、工廠模式、Mixin模式、裝飾模式、亨元(Flyweight)模式、MVC模式、MVP模式、MVVM模式、組合模式、介面卡模式、外觀模式、觀察者模式、迭代器模式、惰性初始模式、代理模式、建造者模式。共23種
二、常見的設計模式例項
-
工廠模式:
- 建構函式+new關鍵字建立物件
- 建立物件的模式
- 批量建立同類物件
- 原料,加工,出廠
function CreateObj(n, a){
this.name = n;
this.age = a;
this.show = function(){
console.log(this.name + "===" + this.age);
}
}
var p1 = new CreateObj("張三",18);
var p2 = new CreateObj("李四",19);
p1.show();
p2.show();
- 單例模式:
- 單個例項
- 當某些功能,需要被重複使用時,無關乎到底有幾個物件,只要有至少一個就可以投入使用
- 寫法不定,只要在重複使用或執行過程中只同時存在一個物件,即可
//多次建立,返回同一個物件
function fn(){
if(!fn.obj) { //給函式新增一個屬性。 因為函式是全域性的,所以該屬性一旦新增一直存在;
fn.obj = {
name : “liyang"
};
}
return fn.obj;
}
var obj1 = new fn();
var obj2 = new fn();
console.log (obj1 == obj2);
- 觀察者模式—(釋出訂閱者模式)
- 定義例一種一對多的關係,讓多個觀察者物件同時監聽某一個主題物件,對這個主題物件的狀態發生變化時就會通知所有的嗎觀察者物件,使得他們能夠自動更新自己。
- 優點:
- 支援簡單的廣播通訊,自動通知所有已經訂閱的物件
- 頁面載入後目標物件很容易與觀察者存在一種動態關聯,增加了靈活性
- 目標物件與觀察者之間的抽象耦合關係能夠單獨擴充套件以及重用
class AddEvent{
constructor(){
// 記錄資訊的小本本
// this.msg = {
// 張三:[扣分,罰錢,俯臥撐],
// 李四:[寫檢討,罰錢]
// }
this.msg = {};
}
add(type, cb){
// 新增某個型別和對應的處理函式
// 新增之前先判斷是否已經存在
if(this.msg[type]){
// 如果存在,就新增
this.msg[type].push(cb);
}else{
// 如果不存在,就建立並賦值
this.msg[type] = [cb];
}
}
remove(type, cb){
// 刪除某個型別的某個處理函式
// 刪除之前先判斷是否存在,不存在就不用執行了
if(!this.msg[type]) return;
// 存在,找到本次要刪除的函式
for(var i=0;i<this.msg[type].length;i++){
if(this.msg[type][i] === cb){
break;
}
}
// 從陣列中剔除
this.msg[type].splice(i,1);
}
emit(type){
// 觸發某個型別的所有的處理函式
// 觸發之前先判斷是否存在,不存在就不用執行了
if(!this.msg[type]) return;
// 存在,拿到每個處理函式
for(var i=0;i<this.msg[type].length;i++){
// 執行
this.msg[type][i]();
}
}
}
var ls = new AddEvent();
ls.add("張三", fn1)
function fn1(){
console.log("扣分")
}
ls.add("張三", fn2)
function fn2(){
console.log("罰錢")
}
ls.add("張三", fn3)
function fn3(){
console.log("俯臥撐")
}
// ls.remove("張三",fn1);
ls.remove("張三",fn2);
// ls.remove("張三",fn3);
ls.emit("張三");
console.log(ls);
ls.add("李四", function(){
console.log("開除")
})
- 策略模式:
- 計劃
- 提前定義或規劃好要執行的功能1,功能2,功能3…在程式執行過程中,根據動態產生的不同的狀態,決定真正要執行的功能
- 介面卡模式:
- 介面卡,容錯
- 介面卡的意義,多數應用在系統介面使用,也就是別人提供了一個功能,但要求傳入一個A型別的引數
- 而我們手裡的資料是B型別的,如果我們想使用這個功能。那麼有兩種解決辦法:
- 第一,把自己的原始碼進行修改,讓B型別改為A型別,這是非常蠢的做法。
- 第二,我們把B型別的資料進行一個包裝,讓它看起來符合型別A,這個包裝函式,就是介面卡
//我們要對所有產品都進行一個標準化的測試,測試流程其中包括了
電話、簡訊、遊戲、音樂等等功能
//但是對於一個平板來說,電話功能是無法使用的,因此測試會出問題。
所以將平板進行了包裝(類似於代理)
//這樣就簡單的解決了相容問題
function test(product) {
try {
product.phonecall();
} catch(e) {
console.log("電話功能測試失敗!")
}
try {
product.playgame();
} catch(e) {
console.log("遊戲功能測試失敗!")
}
}
function Phone(){
this.phonecall = function(){}
this.playgame = function(){}
this.toString = function(){
return "電話";
}
}
function Pad(){
this.playgame = function(){}
this.toString = function(){
return "平板";
}
}
//介面卡的意義,多數應用在系統介面使用,也就是別人提供了一個功能,但要求傳入一個A型別的引數
//而我們手裡的資料是B型別的,如果我們想使用這個功能。那麼有兩種解決辦法:
//第一,把自己的原始碼進行修改,讓B型別改為A型別,這是非常蠢的做法。
//第二,我們把B型別的資料進行一個包裝,讓它看起來符合型別A,這個包裝函式,就是介面卡。
function Adapter(product){
this.phonecall = function(){
if(product.phonecall) {
product.phonecall();
} else {
console.log("this "+ product.toString() + " is not support function phonecall!")
}
}
this.playgame = function(){
if(product.playgame) {
product.playgame();
}
}
}
test(new Phone());
test(new Adapter(new Pad()));
- 組合模式
- 組合物件或類,為了實現批量操作
- 組合成樹狀結構,類似於HTML結構的結構
- 組合模式就是為動態的HTML而生的
- 組合器,用來將物件組合起來
function ImagesStore( id ){
// 準備用來儲存子物件(枝物件或葉物件)
this.children = [];
this.element = document.createElement("div");
this.element.id = id;
document.body.appendChild(this.element)
}
ImagesStore.prototype = {
constructor : ImagesStore,
add:function( child ){
this.children.push( child );
this.element.appendChild( child.getElement() );
},
remove:function( child ){
for( var i=0; i< this.children.length; i++ ){
if( this.children[i] === child ){
this.children.splice( i, 1 );
break;
}
}
this.element.removeChild( child.getElement() );
},
show:function(){
this.element.style.border = 'solid 10px black';
for( var i=0; i<this.children.length; i++ ){
this.children[i].show();
}
},
hide:function(){
for( var i=0; i<this.children.length; i++ ){
this.children[i].hide();
}
this.element.style.border = 'none';
},
getElement:function(){
return this.element;
}
}
function Son(img){
this.abc = document.createElement("img");
this.abc.src = img;
}
Son.prototype = {
constructor: Son,
add:function( child ){
console.log("這是一個葉物件,無法新增子物件了")
},
remove:function( child ){
console.log("這是一個葉物件,無法刪除子物件了")
},
show:function(){
this.abc.style.border = "solid 10px #aaa";
},
hide:function(){
this.abc.style.border = "none";
},
getElement:function(){
return this.abc;
}
}
var s1 = new Son("https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2926199504,3558053336&fm=26&gp=0.jpg");
var s2 = new Son("https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3153405721,1524067674&fm=26&gp=0.jpg");
var p = new ImagesStore("box1");
p.add(s1);
p.add(s2);
p.show();
- 代理模式
- 代理,委託,自己不想做,交給別人做
- 為了攔截專案中某些已經固定執行模式的資料或資訊,進行二次使用或另作他用
- 代理模式分成兩個部分,一個部分是本體; 即為你想要實現的功能;另一部分為代理;代理可以代替本體例項化;
代理一般使用在非常耗時的資料上,也會用在體積十分龐大的本體上。 - 一句話總結代理模式:為其他物件提供代理,以控制這個物件的訪問;
//舉個簡單的例子:
//小夥子想要送花給小姑娘,但是不好意思送,於是找到了快遞小哥,小哥幫忙送花;在這裡小哥就是代理!
//小姑娘(姑娘的名字);
vargirl=function(name){
this.name=name;
}
//小夥子(想要送給誰);
varboy=function(girl){
this.girl=girl;
this.sendGift=function(gift){
alert("你好,漂亮的"+this.girl.name+",這是我送你的:"+gift);
}
}
//快遞小哥(知道送給誰);
varporxyLitterBrother=function(girl){
this.girl=girl;
this.send=function(gift){
gift="一個吻";
(newboy(girl)).sendGift(gift);
}
}
現在的送花就變得簡單了;
varporxy= newporxyLitterBrother(newgirl("花花"));
porxy.send("鑽戒");
一個人替你去做一些事情。 這個人保不齊還能撈點好處, 這就是代理模式;
- MVC模式
- M:model:模型,管理資料
- V:view:檢視,管理資料在頁面上的渲染方式
- C:ctrl:控制器,中央控制中心
- 工作流程:
- 使用者向控制器傳送一個指令
- 控制器根據指令到模型中取出對應的資料並儲存
- 控制器根據指令到檢視中取出一個檢視並將資料發給這個檢視
- 檢視決定資料以何種方式呈現給使用者
class Model{
getData1(){
return "hello";
}
getData2(){
return "設計模式";
}
getData3(){
return "和";
}
getData4(){
return "即將到來的";
}
getData5(){
return "jQuery";
}
}
class View{
view1(d){
console.log(d);
}
view2(d){
alert(d);
}
view3(d){
document.write(d);
}
}
class Ctrl{
constructor(){
this.m = new Model();
this.v = new View();
}
ctrl1(){
this.d = this.m.getData3();
this.v.view2(this.d);
}
ctrl2(){
this.d = this.m.getData1();
this.v.view3(this.d);
}
ctrl3(){
this.d = this.m.getData2();
this.v.view1(this.d);
}
ctrl4(){
this.d = this.m.getData4();
this.v.view2(this.d);
}
}
var c = new Ctrl();
c.ctrl3();