1. 程式人生 > >詳解 Javascript十大常用設計模式

詳解 Javascript十大常用設計模式

轉載自:https://juejin.im/entry/58c280b1da2f600d8725b887

一:理解工廠模式

   工廠模式類似於現實生活中的工廠可以產生大量相似的商品,去做同樣的事情,實現同樣的效果;這時候需要使用工廠模式。

   簡單的工廠模式可以理解為解決多個相似的問題;這也是她的優點;比如如下程式碼: 

function CreatePerson(name,age,sex) {
    var obj = new Object();
    obj.name = name;
    obj.age = age;
    obj.sex = sex;
    obj.sayName = function(){
        return this.name;
    }
    return obj;
}
var p1 = new CreatePerson("longen",'28','男');
var p2 = new CreatePerson("tugenhua",'27','女');
console.log(p1.name); // longen
console.log(p1.age);  // 28
console.log(p1.sex);  // 男
console.log(p1.sayName()); // longen

console.log(p2.name);  // tugenhua
console.log(p2.age);   // 27
console.log(p2.sex);   // 女
console.log(p2.sayName()); // tugenhua

// 返回都是object 無法識別物件的型別 不知道他們是哪個物件的實列
console.log(typeof p1);  // object
console.log(typeof p2);  // object
console.log(p1 instanceof Object); // true

如上程式碼:函式CreatePerson能接受三個引數name,age,sex等引數,可以無數次呼叫這個函式,每次返回都會包含三個屬性和一個方法的物件。

工廠模式是為了解決多個類似物件宣告的問題;也就是為了解決實列化物件產生重複的問題。

優點:能解決多個相似的問題。

缺點:不能知道物件識別的問題(物件的型別不知道)。

複雜的工廠模式定義是:將其成員物件的實列化推遲到子類中,子類可以重寫父類介面方法以便建立的時候指定自己的物件型別。

 父類只對建立過程中的一般性問題進行處理,這些處理會被子類繼承,子類之間是相互獨立的,具體的業務邏輯會放在子類中進行編寫。

 父類就變成了一個抽象類,但是父類可以執行子類中相同類似的方法,具體的業務邏輯需要放在子類中去實現;比如我現在開幾個自行車店,那麼每個店都有幾種型號的自行車出售。我們現在來使用工廠模式來編寫這些程式碼;

父類的建構函式如下:

// 定義自行車的建構函式
var BicycleShop = function(){};
BicycleShop.prototype = {
    constructor: BicycleShop,
    /*
    * 買自行車這個方法
    * @param {model} 自行車型號
    */
    sellBicycle: function(model){
        var bicycle = this.createBicycle(mode);
        // 執行A業務邏輯
        bicycle.A();

        // 執行B業務邏輯
        bicycle.B();

        return bicycle;
    },
    createBicycle: function(model){
        throw new Error("父類是抽象類不能直接呼叫,需要子類重寫該方法");
    }
};

上面是定義一個自行車抽象類來編寫工廠模式的實列,定義了createBicycle這個方法,但是如果直接例項化父類,呼叫父類中的這個createBicycle 方法,會丟擲一個error,因為父類是一個抽象類,他不能被實列化,只能通過子類來實現這個方法,實現自己的業務邏輯,下面我們來定義子類,我們學會如何使用工廠模式重新編寫這個方法,首先我們需要繼承父類中的成員,然後編寫子類 ;如下程式碼:

// 定義自行車的建構函式
var BicycleShop = function(name){
    this.name = name;
    this.method = function(){
        return this.name;
    }
};
BicycleShop.prototype = {
    constructor: BicycleShop,
    /*
     * 買自行車這個方法
     * @param {model} 自行車型號
    */
    sellBicycle: function(model){
            var bicycle = this.createBicycle(model);
            // 執行A業務邏輯
            bicycle.A();

            // 執行B業務邏輯
            bicycle.B();

            return bicycle;
        },
        createBicycle: function(model){
            throw new Error("父類是抽象類不能直接呼叫,需要子類重寫該方法");
        }
    };
    // 實現原型繼承
    function extend(Sub,Sup) {
        //Sub表示子類,Sup表示超類
        // 首先定義一個空函式
        var F = function(){};

        // 設定空函式的原型為超類的原型
        F.prototype = Sup.prototype; 

        // 例項化空函式,並把超類原型引用傳遞給子類
        Sub.prototype = new F();
                    
        // 重置子類原型的構造器為子類自身
        Sub.prototype.constructor = Sub;
                    
        // 在子類中儲存超類的原型,避免子類與超類耦合
        Sub.sup = Sup.prototype;

        if(Sup.prototype.constructor === Object.prototype.constructor) {
            // 檢測超類原型的構造器是否為原型自身
            Sup.prototype.constructor = Sup;
        }
    }
    var BicycleChild = function(name){
        this.name = name;
// 繼承建構函式父類中的屬性和方法
        BicycleShop.call(this,name);
    };
    // 子類繼承父類原型方法
    extend(BicycleChild,BicycleShop);
// BicycleChild 子類重寫父類的方法
BicycleChild.prototype.createBicycle = function(){
    var A = function(){
        console.log("執行A業務操作");    
    };
    var B = function(){
        console.log("執行B業務操作");
    };
    return {
        A: A,
        B: B
    }
}
var childClass = new BicycleChild("龍恩");
console.log(childClass);

例項化子類,然後打印出該例項, 如下截圖所示:

console.log(childClass.name);  // 龍恩

// 下面是例項化後 執行父類中的sellBicycle這個方法後會依次呼叫父類中的A

// 和B方法;A方法和 B方法依次在子類中去編寫具體的業務邏輯。

childClass.sellBicycle("mode"); // 打印出  執行A業務操作和執行 B業務操作

上面只是"龍恩"自行車這麼一個型號的,如果需要生成其他型號的自行車的話,可以編寫其他子類,工廠模式最重要的優點是:可以實現一些相同的方法,這些相同的方法我們可以放在父類中編寫程式碼,那麼需要實現具體的業務邏輯,那麼可以放在子類中重寫該父類的方法,去實現自己的業務邏輯;使用專業術語來講的話有 2點:第一:弱化物件間的耦合,防止程式碼的重複。在一個方法中進行類的例項化,可以消除重複性的程式碼。第二:重複性的程式碼可以放在父類去編寫,子類繼承於父類的所有成員屬性和方法,子類只專注於實現自己的業務邏輯。

回到頂部

二:理解單體模式

單體模式提供了一種將程式碼組織為一個邏輯單元的手段,這個邏輯單元中的程式碼可以通過單一變數進行訪問。

單體模式的優點是:

  1. 可以用來劃分名稱空間,減少全域性變數的數量。
  2. 使用單體模式可以使程式碼組織的更為一致,使程式碼容易閱讀和維護。
  3. 可以被例項化,且例項化一次。

什麼是單體模式?單體模式是一個用來劃分名稱空間並將一批屬性和方法組織在一起的物件,如果它可以被例項化,那麼它只能被例項化一次。

但是並非所有的物件字面量都是單體,比如說模擬陣列或容納資料的話,那麼它就不是單體,但是如果是組織一批相關的屬性和方法在一起的話,那麼它有可能是單體模式,所以這需要看開發者編寫程式碼的意圖;

下面我們來看看定義一個物件字面量(結構類似於單體模式)的基本結構如下:

// 物件字面量
var Singleton = {
    attr1: 1,
    attr2: 2,
    method1: function(){
        return this.attr1;
    },
    method2: function(){
        return this.attr2;
    }
};

如上面只是簡單的字面量結構,上面的所有成員變數都是通過Singleton來訪問的,但是它並不是單體模式;因為單體模式還有一個更重要的特點,就是可以僅被例項化一次,上面的只是不能被例項化的一個類,因此不是單體模式;物件字面量是用來建立單體模式的方法之一;

使用單體模式的結構如下demo

我們明白的是單體模式如果有例項化的話,那麼只例項化一次,要實現一個單體模式的話,我們無非就是使用一個變數來標識該類是否被例項化,如果未被例項化的話,那麼我們可以例項化一次,否則的話,直接返回已經被例項化的物件。

如下程式碼是單體模式的基本結構:

// 單體模式
var Singleton = function(name){
    this.name = name;
    this.instance = null;
};
Singleton.prototype.getName = function(){
    return this.name;
}
// 獲取例項物件
function getInstance(name) {
    if(!this.instance) {
        this.instance = new Singleton(name);
    }
    return this.instance;
}
// 測試單體模式的例項
var a = getInstance("aa");
var b = getInstance("bb");

// 因為單體模式是隻例項化一次,所以下面的例項是相等的

console.log(a === b); // true

由於單體模式只例項化一次,因此第一次呼叫,返回的是a例項物件,當我們繼續呼叫的時候,b的例項就是a 的例項,因此下面都是列印的是aa;

console.log(a.getName());// aa

console.log(b.getName());// aa

上面的封裝單體模式也可以改成如下結構寫法:

// 單體模式
var Singleton = function(name){
    this.name = name;
};
Singleton.prototype.getName = function(){
    return this.name;
}
// 獲取例項物件
var getInstance = (function() {
    var instance = null;
    return function(name) {
        if(!instance) {
            instance = new Singleton(name);
        }
        return instance;
    }
})();
// 測試單體模式的例項
var a = getInstance("aa");
var b = getInstance("bb");

// 因為單體模式是隻例項化一次,所以下面的例項是相等的

console.log(a === b); // true

console.log(a.getName());// aa

console.log(b.getName());// aa

理解使用代理實現單列模式的好處
    比如我現在頁面上需要建立一個div的元素,那麼我們肯定需要有一個建立 div的函式,而現在我只需要這個函式只負責建立div元素,其他的它不想管,也就是想實現單一職責原則,就好比淘寶的kissy 一樣,一開始的時候他們定義kissy只做一件事,並且把這件事做好,具體的單體模式中的例項化類的事情交給代理函式去處理,這樣做的好處是具體的業務邏輯分開了,代理只管代理的業務邏輯,在這裡代理的作用是例項化物件,並且只例項化一次;  建立div程式碼只管建立div,其他的不管;如下程式碼:

// 單體模式
var CreateDiv = function(html) {
    this.html = html;
    this.init();
}
CreateDiv.prototype.init = function(){
    var div = document.createElement("div");
    div.innerHTML = this.html;
    document.body.appendChild(div);
};
// 代理實現單體模式
var ProxyMode = (function(){
    var instance;
    return function(html) {
        if(!instance) {
            instance = new CreateDiv("我來測試下");
        }
        return instance;
    } 
})();
var a = new ProxyMode("aaa");
var b = new ProxyMode("bbb");
console.log(a===b);// true

理解使用單體模式來實現彈窗的基本原理

下面我們繼續來使用單體模式來實現一個彈窗的demo;我們先不討論使用單體模式來實現,我們想下我們平時是怎麼編寫程式碼來實現彈窗效果的; 比如我們有一個彈窗,預設的情況下肯定是隱藏的,當我點選的時候,它需要顯示出來;如下編寫程式碼:

// 實現彈窗
var createWindow = function(){
    var div = document.createElement("div");
    div.innerHTML = "我是彈窗內容";
    div.style.display = 'none';
    document.body.appendChild('div');
    return div;
};
document.getElementById("Id").onclick = function(){
    // 點選後先建立一個div元素
    var win = createWindow();
    win.style.display = "block";
}

如上的程式碼;大家可以看看,有明顯的缺點,比如我點選一個元素需要建立一個div,我點選第二個元素又會建立一次div,我們頻繁的點選某某元素,他們會頻繁的建立div的元素,雖然當我們點選關閉的時候可以移除彈出程式碼,但是呢我們頻繁的建立和刪除並不好,特別對於效能會有很大的影響,對DOM頻繁的操作會引起重繪等,從而影響效能;因此這是非常不好的習慣;我們現在可以使用單體模式來實現彈窗效果,我們只例項化一次就可以了;如下程式碼:

// 實現單體模式彈窗
var createWindow = (function(){
    var div;
    return function(){
        if(!div) {
            div = document.createElement("div");
            div.innerHTML = "我是彈窗內容";
            div.style.display = 'none';
            document.body.appendChild(div);
        }
        return div;
    }
})();
document.getElementById("Id").onclick = function(){
    // 點選後先建立一個div元素
    var win = createWindow();
    win.style.display = "block";
}

理解編寫通用的單體模式

上面的彈窗的程式碼雖然完成了使用單體模式建立彈窗效果,但是程式碼並不通用,比如上面是完成彈窗的程式碼,假如我們以後需要在頁面中一個iframe呢?我們是不是需要重新寫一套建立iframe的程式碼呢?比如如下建立iframe:

var createIframe = (function(){
    var iframe;
    return function(){
        if(!iframe) {
            iframe = document.createElement("iframe");
            iframe.style.display = 'none';
            document.body.appendChild(iframe);
        }
        return iframe;
    };
})();

我們看到如上程式碼,建立div的程式碼和建立iframe程式碼很類似,我們現在可以考慮把通用的程式碼分離出來,使程式碼變成完全抽象,我們現在可以編寫一套程式碼封裝在getInstance函式內,如下程式碼:

var getInstance = function(fn) {
    var result;
    return function(){
        return result || (result = fn.call(this,arguments));
    }
};

如上程式碼:我們使用一個引數fn傳遞進去,如果有result這個例項的話,直接返回,否則的話,當前的getInstance函式呼叫fn這個函式,是this指標指向與這個fn這個函式;之後返回被儲存在result裡面;現在我們可以傳遞一個函式進去,不管他是建立div也好,還是建立iframe也好,總之如果是這種的話,都可以使用getInstance來獲取他們的例項物件;

如下測試建立iframe和建立div的程式碼如下:

// 建立div
var createWindow = function(){
    var div = document.createElement("div");
    div.innerHTML = "我是彈窗內容";
    div.style.display = 'none';
    document.body.appendChild(div);
    return div;
};
// 建立iframe
var createIframe = function(){
    var iframe = document.createElement("iframe");
    document.body.appendChild(iframe);
    return iframe;
};
// 獲取例項的封裝程式碼
var getInstance = function(fn) {
    var result;
    return function(){
        return result || (result = fn.call(this,arguments));
    }
};
// 測試建立div
var createSingleDiv = getInstance(createWindow);
document.getElementById("Id").onclick = function(){
    var win = createSingleDiv();
    win.style.display = "block";
};
// 測試建立iframe
var createSingleIframe = getInstance(createIframe);
document.getElementById("Id").onclick = function(){
    var win = createSingleIframe();
    win.src = "http://cnblogs.com";
};
回到頂部

三:理解模組模式

我們通過單體模式理解了是以物件字面量的方式來建立單體模式的;比如如下的物件字面量的方式程式碼如下:

var singleMode = {
    name: value,
    method: function(){
                
    }
};

模組模式的思路是為單體模式新增私有變數和私有方法能夠減少全域性變數的使用;如下就是一個模組模式的程式碼結構:

var singleMode = (function(){
    // 建立私有變數
    var privateNum = 112;
    // 建立私有函式
    function privateFunc(){
        // 實現自己的業務邏輯程式碼
    }
    // 返回一個物件包含公有方法和屬性
    return {
        publicMethod1: publicMethod1,
        publicMethod2: publicMethod1
    };
})();

   模組模式使用了一個返回物件的匿名函式。在這個匿名函式內部,先定義了私有變數和函式,供內部函式使用,然後將一個物件字面量作為函式的值返回,返回的物件字面量中只包含可以公開的屬性和方法。這樣的話,可以提供外部使用該方法;由於該返回物件中的公有方法是在匿名函式內部定義的,因此它可以訪問內部的私有變數和函式。

我們什麼時候使用模組模式?

如果我們必須建立一個物件並以某些資料進行初始化,同時還要公開一些能夠訪問這些私有資料的方法,那麼我們這個時候就可以使用模組模式了。

理解增強的模組模式

增強的模組模式的使用場合是:適合那些單列必須是某種型別的例項,同時還必須新增某些屬性或方法對其加以增強的情況。比如如下程式碼:

function CustomType() {
    this.name = "tugenhua";
};
CustomType.prototype.getName = function(){
    return this.name;
}
var application = (function(){
    // 定義私有
    var privateA = "aa";
    // 定義私有函式
    function A(){};

    // 例項化一個物件後,返回該例項,然後為該例項增加一些公有屬性和方法
    var object = new CustomType();

    // 新增公有屬性
    object.A = "aa";
    // 新增公有方法
    object.B = function(){
        return privateA;
    }
    // 返回該物件
    return object;
})();

下面我們來列印下application該物件;如下:

console.log(application);

繼續列印該公有屬性和方法如下:

console.log(application.A);// aa

console.log(application.B()); // aa

console.log(application.name); // tugenhua

console.log(application.getName());// tugenhua

回到頂部

四:理解代理模式

     代理是一個物件,它可以用來控制對本體物件的訪問,它與本體物件實現了同樣的介面,代理物件會把所有的呼叫方法傳遞給本體物件的;代理模式最基本的形式是對訪問進行控制,而本體物件則負責執行所分派的那個物件的函式或者類,簡單的來講本地物件注重的去執行頁面上的程式碼,代理則控制本地物件何時被例項化,何時被使用;我們在上面的單體模式中使用過一些代理模式,就是使用代理模式實現單體模式的例項化,其他的事情就交給本體物件去處理;

代理的優點:

  1. 代理物件可以代替本體被例項化,並使其可以被遠端訪問;
  2. 它還可以把本體例項化推遲到真正需要的時候;對於例項化比較費時的本體物件,或者因為尺寸比較大以至於不用時不適於儲存在記憶體中的本體,我們可以推遲例項化該物件;

我們先來理解代理物件代替本體物件被例項化的列子;比如現在京東ceo想送給奶茶妹一個禮物,但是呢假如該ceo不好意思送,或者由於工作忙沒有時間送,那麼這個時候他就想委託他的經紀人去做這件事,於是我們可以使用代理模式來編寫如下程式碼:

// 先申明一個奶茶妹物件
var TeaAndMilkGirl = function(name) {
    this.name = name;
};
// 這是京東ceo先生
var Ceo = function(girl) {
    this.girl = girl;
    // 送結婚禮物 給奶茶妹
    this.sendMarriageRing = function(ring) {
        console.log("Hi " + this.girl.name + ", ceo送你一個禮物:" + ring);
    }
};
// 京東ceo的經紀人是代理,來代替送
var ProxyObj = function(girl){
    this.girl = girl;
    // 經紀人代理送禮物給奶茶妹
    this.sendGift = function(gift) {
        // 代理模式負責本體物件例項化
        (new Ceo(this.girl)).sendMarriageRing(gift);
    }
};
// 初始化
var proxy = new ProxyObj(new TeaAndMilkGirl("奶茶妹"));
proxy.sendGift("結婚戒"); // Hi 奶茶妹, ceo送你一個禮物:結婚戒

程式碼如上的基本結構,TeaAndMilkGirl 是一個被送的物件(這裡是奶茶妹);Ceo 是送禮物的物件,他儲存了奶茶妹這個屬性,及有一個自己的特權方法sendMarriageRing 就是送禮物給奶茶妹這麼一個方法;然後呢他是想通過他的經紀人去把這件事完成,於是需要建立一個經濟人的代理模式,名字叫ProxyObj ;他的主要做的事情是,把ceo交給他的禮物送給ceo的情人,因此該物件同樣需要儲存ceo情人的物件作為自己的屬性,同時也需要一個特權方法sendGift ,該方法是送禮物,因此在該方法內可以例項化本體物件,這裡的本體物件是ceo送花這件事情,因此需要例項化該本體物件後及呼叫本體物件的方法(sendMarriageRing).

最後我們初始化是需要代理物件ProxyObj;呼叫ProxyObj 物件的送花這個方法(sendGift)即可;

對於我們提到的優點,第二點的話,我們下面可以來理解下虛擬代理,虛擬代理用於控制對那種建立開銷很大的本體訪問,它會把本體的例項化推遲到有方法被呼叫的時候;比如說現在有一個物件的例項化很慢的話,不能在網頁載入的時候立即完成,我們可以為其建立一個虛擬代理,讓他把該物件的例項推遲到需要的時候。

理解使用虛擬代理實現圖片的預載入

在網頁開發中,圖片的預載入是一種比較常用的技術,如果直接給img標籤節點設定src屬性的話,如果圖片比較大的話,或者網速相對比較慢的話,那麼在圖片未載入完之前,圖片會有一段時間是空白的場景,這樣對於使用者體驗來講並不好,那麼這個時候我們可以在圖片未載入完之前我們可以使用一個 loading載入圖片來作為一個佔位符,來提示使用者該圖片正在載入,等圖片載入完後我們可以對該圖片直接進行賦值即可;下面我們先不用代理模式來實現圖片的預載入的情況下程式碼如下:

第一種方案:不使用代理的預載入圖片函式如下

// 不使用代理的預載入圖片函式如下
var myImage = (function(){
    var imgNode = document.createElement("img");
    document.body.appendChild(imgNode);
    var img = new Image();
    img.onload = function(){
        imgNode.src = this.src;
    };
    return {
        setSrc: function(src) {
            imgNode.src = "http://img.lanrentuku.com/img/allimg/1212/5-121204193Q9-50.gif";
            img.src = src;
        }
    }
})();
// 呼叫方式
myImage.setSrc("https://img.alicdn.com/tps/i4/TB1b_neLXXXXXcoXFXXc8PZ9XXX-130-200.png");

如上程式碼是不使用代理模式來實現的程式碼;

第二種方案:使用代理模式來編寫預載入圖片的程式碼如下:

var myImage = (function(){
    var imgNode = document.createElement("img");
    document.body.appendChild(imgNode);
    return {
        setSrc: function(src) {
            imgNode.src = src;
        }
    }
})();
// 代理模式
var ProxyImage = (function(){
    var img = new Image();
    img.onload = function(){
        myImage.setSrc(this.src);
    };
    return {
        setSrc: function(src) {
                         myImage.setSrc("http://img.lanrentuku.com/img/allimg/1212/5-121204193Q9-50.gif");
        img.src = src;
        }
    }
})();
// 呼叫方式
ProxyImage.setSrc("https://img.alicdn.com/tps/i4/TB1b_neLXXXXXcoXFXXc8PZ9XXX-130-200.png");

第一種方案是使用一般的編碼方式實現圖片的預載入技術,首先建立imgNode元素,然後呼叫myImage.setSrc該方法的時候,先給圖片一個預載入圖片,當圖片載入完的時候,再給img元素賦值,第二種方案是使用代理模式來實現的,myImage 函式只負責建立img元素,代理函式ProxyImage 負責給圖片設定loading圖片,當圖片真正載入完後的話,呼叫myImage中的myImage.setSrc方法設定圖片的路徑;他們之間的優缺點如下:

  1. 第一種方案一般的方法程式碼的耦合性太高,一個函式內負責做了幾件事情,比如建立img元素,和實現給未載入圖片完成之前設定loading載入狀態等多項事情,未滿足面向物件設計原則中單一職責原則;並且當某個時候不需要代理的時候,需要從myImage 函式內把程式碼刪掉,這樣程式碼耦合性太高。
  2. 第二種方案使用代理模式,其中myImage 函式只負責做一件事,建立img元素加入到頁面中,其中的載入loading圖片交給代理函式ProxyImage 去做,當圖片載入成功後,代理函式ProxyImage 會通知及執行myImage 函式的方法,同時當以後不需要代理物件的話,我們直接可以呼叫本體物件的方法即可;

從上面代理模式我們可以看到,代理模式和本體物件中有相同的方法setSrc,這樣設定的話有如下2個優點:

  1. 使用者可以放心地請求代理,他們只關心是否能得到想要的結果。假如我門不需要代理物件的話,直接可以換成本體物件呼叫該方法即可。
  2. 在任何使用本體物件的地方都可以替換成使用代理。

當然如果代理物件和本體物件都返回一個匿名函式的話,那麼也可以認為他們也具有一直的介面;比如如下程式碼:

var myImage = (function(){
    var imgNode = document.createElement("img");
    document.body.appendChild(imgNode);
    return function(src){
        imgNode.src = src; 
    }
})();
// 代理模式
var ProxyImage = (function(){
    var img = new Image();
    img.onload = function(){
        myImage(this.src);
    };
    return function(src) {
                myImage("http://img.lanrentuku.com/img/allimg/1212/5-121204193Q9-50.gif");
        img.src = src;
    }
})();
// 呼叫方式
ProxyImage("https://img.alicdn.com/tps/i4/TB1b_neLXXXXXcoXFXXc8PZ9XXX-130-200.png");

虛擬代理合並http請求的理解:

   比如在做後端系統中,有表格資料,每一條資料前面有複選框按鈕,當點選複選框按鈕時候,需要獲取該id後需要傳遞給給伺服器傳送ajax請求,伺服器端需要記錄這條資料,去請求,如果我們每當點選一下向伺服器傳送一個http請求的話,對於伺服器來說壓力比較大,網路請求比較頻繁,但是如果現在該系統的實時資料不是很高的話,我們可以通過一個代理函式收集一段時間內(比如說2-3秒)的所有id,一次性發ajax請求給伺服器,相對來說網路請求降低了, 伺服器壓力減少了;

// 首先html結構如下:
<p>
    <label>選擇框</label>
    <input type="checkbox" class="j-input" data-id="1"/>
</p>
<p>
    <label>選擇框</label>
    <input type="checkbox" class="j-input" data-id = "2"/>
</p>
<p>
    <label>選擇框</label>
    <input type="checkbox" class="j-input" data-id="3"/>
</p>
<p>
    <label>選擇框</label>
    <input type="checkbox" class="j-input" data-id = "4"/>
</p>

一般的情況下 JS如下編寫

<script>
    var checkboxs = document.getElementsByClassName("j-input");
    for(var i = 0,ilen = checkboxs.length; i < ilen; i+=1) {
        (function(i){
            checkboxs[i].onclick = function(){
                if(this.checked) {
                    var id = this.getAttribute("data-id");
                    // 如下是ajax請求
                }
            }
        })(i);
    }
</script>

下面我們通過虛擬代理的方式,延遲2秒,在2秒後獲取所有被選中的複選框的按鈕id,一次性給伺服器發請求。

  通過點選頁面的複選框,選中的時候增加一個屬性isflag,沒有選中的時候刪除該屬性isflag,然後延遲個2秒,在2秒後重新判斷頁面上所有複選框中有isflag的屬性上的id,存入陣列,然後代理函式呼叫本體函式的方法,把延遲2秒後的所有id一次性發給本體方法,本體方法可以獲取所有的id,可以向伺服器端傳送ajax請求,這樣的話,伺服器的請求壓力相對來說減少了。

程式碼如下:

// 本體函式
var mainFunc = function(ids) {
    console.log(ids); // 即可列印被選中的所有的id
    // 再把所有的id一次性發ajax請求給伺服器端
};
// 代理函式 通過代理函式獲取所有的id 傳給本體函式去執行
var proxyFunc = (function(){
    var cache = [],  // 儲存一段時間內的id
        timer = null; // 定時器
    return function(checkboxs) {
        // 判斷如果定時器有的話,不進行覆蓋操作
        if(timer) {
            return;
        }
        timer = setTimeout(function(){
            // 在2秒內獲取所有被選中的id,通過屬性isflag判斷是否被選中
            for(var i = 0,ilen = checkboxs.length; i < ilen; i++) {
                if(checkboxs[i].hasAttribute("isflag")) {
                    var id = checkboxs[i].getAttribute("data-id");
                    cache[cache.length] = id;
                }
            }
            mainFunc(cache.join(',')); // 2秒後需要給本體函式傳遞所有的id
            // 清空定時器
            clearTimeout(timer);
            timer = null;
            cache = [];
        },2000);
    }
})();
var checkboxs = document.getElementsByClassName("j-input");
for(var i = 0,ilen = checkboxs.length; i < ilen; i+=1) {
    (function(i){
        checkboxs[i].onclick = function(){
            if(this.checked) {
                // 給當前增加一個屬性
                this.setAttribute("isflag",1);
            }else {
                this.removeAttribute('isflag');
            }
            // 呼叫代理函式
            proxyFunc(checkboxs);
        }
    })(i);
}

理解快取代理:

   快取代理的含義就是對第一次執行時候進行快取,當再一次執行相同的時候,直接從快取裡面取,這樣做的好處是避免重複一次運算功能,如果運算非常複雜的話,對效能很耗費,那麼使用快取物件可以提高效能;我們可以先來理解一個簡單的快取列子,就是網上常見的加法和乘法的運算。程式碼如下:


            
           

相關推薦

Javascript常用設計模式

轉載自:https://juejin.im/entry/58c280b1da2f600d8725b887一:理解工廠模式   工廠模式類似於現實生活中的工廠可以產生大量相似的商品,去做同樣的事情,實現同樣的效果;這時候需要使用工廠模式。   簡單的工廠模式可以理解為解決多個相

觀察者模式——參考《Head First設計模式

觀察者模式的定義 定義了物件之間的一對多依賴,這樣一來,當一個物件狀態改變時,它的所有依賴者都會受到通知並自動更新。 觀察者模式的分析 本文是以氣象站系統為例說明觀察者模式的,簡單來說就是氣象收集到的資料組成了一個數據中心(也就是觀察者模式中的主題),不同的佈告板(觀察者)

常用設計模式,附設計系統思路圖

UML語言: 補 充:spring帶來了面向切面程式設計,在屬性和行為中切一些代理方法或者屬性進去;是對面向物件程式設計的有益補充; 設計7原則: 1、開閉原則:軟體對擴充套件是開放的,對修改是關閉的;【這是一個綱領性原則,最後要達到的結果就是強內聚,少耦合;】

Android 常用設計模式(二) -- 單例模式()

上一篇講到策略模式,變動的程式碼需要用到策略模式,感興趣的小夥伴可以看看. 傳送門:Android 常用設計模式之 – 策略模式 單例模式的定義就不解釋過多了,相信很多小夥伴在設計的時候,都用到這個模式;常用的場景為 資料庫的訪問,檔案流的訪問以及網路連

Javascript六種常用設計模式

單例模式 何為單例模式,就是無論執行多少次函式,都只會生成一個物件哈哈,看一個簡單的demo function Instance(name) { this.name = name; } Instance.prototype.fire = function () { console.log(

JavaScript常用設計模式

OS names his 方法 end nta imp androi 關系 單例模式:確保類只能被實例化一次。 var obj = {} 2、函數返回值 var func = function () {return {}} var obj = func();

JavaScript常用設計模式及程式設計技巧(ES6描述)

前言 平時的開發中可能不太需要用到設計模式,但是 JS 用上設計模式對於效能優化和專案工程化也是很有幫助的,下面就對常用的設計模式進行簡單的介紹與總結。 1. 單例模式 定義:保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。 class Singleton { constructor(

JAVA設計模式(三)----------裝飾者模式

今天LZ帶給大家的是裝飾者模式,提起這個設計模式,LZ心裡一陣激動,這是LZ學習JAVA以來接觸的第一個設計模式,也許也是各位接觸的第一個設計模式。記得當初老師在講IO的時候就提到過它:“是你還有你,一切拜託你。”沒錯,這就是裝飾者模式最簡潔的定義了。下面LZ引出標準的定義(

7 種 Javascript 常用設計模式學習筆記

7 種 Javascript 常用設計模式學習筆記 由於 JS 或者前端的場景限制,並不是 23 種設計模式都常用。 有的是沒有使用場景,有的模式使用場景非常少,所以只是列舉 7 個常見的模式 本文的脈絡: 設計與模式 5 大設計原則 7 種常見的設計模式 一句話解釋含

Linux下套接字)---epoll模式下的IO多路複用伺服器

1 epoll模型簡介 epoll可是當前在Linux下開發大規模併發網路程式的熱門人選,epoll 在Linux2.6核心中正式引入,和select相似,其實都I/O多路複用技術而已,並沒有什麼神祕的。 其實在Linux下設計併發網路程式,向來不缺少

JavaScript中常見的五種設計模式

在程式設計中有很多實用的設計模式,而其中大部分語言的實現都是基於“類”。 在JavaScript中並沒有類這種概念,JS中的函式屬於一等物件,在JS中定義一個物件非常簡單(var obj = {}),而基於JS中閉包與弱型別等特性,在實現一些設計模式的方式上與眾不同。 本

javascript常用設計模式介紹,實現及實際應用(一)

javascript設計模式介紹,實現及實際應用(一) 本文將介紹javascript中常用的設計模式原理和實現,並結合例項講解其應用。 本篇文章先介紹單例模式,策略模式,代理模式,釋出訂閱模式和命令模式,其它幾種模式後續文章將繼續介紹。 1、單例模式 單例模式

微服務設計實現時的常見反模式和陷阱

資料驅動遷移反模式(Data-Driven Migration) 如上圖所示,此種反模式的問題在於微服務的粒度沒有最終確

PHP常用設計模式匯總

設計模式 strac == 抽象 try 抽象類 設計 eal decorator 裝飾模式: <?php abstract class Tile { abstract function getWealthFactor(); } class Plains ext

javascript 中的比較(==和===)

不一致 mit 如果 asc onu tin 算法 復雜 undefine 抽象相等比較算法 比較運算 x==y, 其中 x 和 y 是值,產生 true 或者 false。這樣的比較按如下方式進行: 若 Type(x) 與 Type(y) 相同, 則 若 Type(x)

javascriptjavascript閉包 — 大家準備好瓜子,我要開始講故事啦~~

重要 參數 銷毀 弘揚 它的 bject 標題 多層嵌套 早就 前言: 在這篇文章裏,我將對那些在各種有關閉包的資料中頻繁出現,但卻又千篇一律,且曖昧模糊得讓人難以理解的表述,做一次自己的解讀。或者說是對“紅寶書”的《函數表達式/閉包》的那一章節所寫

機器學習常用算法

不同 youtube 多少 ear 邏輯回歸 total 組成 ddc 表達 機器學習十大常用算法小結 機器學習十大常用算法小結 通過本篇文章可以對ML的常用算法有個常識性的認識,沒有代碼,沒有復雜的理論推導,就是圖解一下,知道這些算法是什麽,它們是怎麽應用的,例子

JAVA常用設計模式

ipp targe aec atd jmh 設計模式 模式 java left 巴揭殼景枚嫡倚濫煽吠徑http://www.docin.com/vxuo069 蔚贍卣碩狼吠亢照某嶽魏徹http://www.docin.com/idj90593 黃濁偃由友兔兆凍寺敬踴酌沮持h

常用設計模式:適配器模式

targe apt aio 什麽是 兼容 概念 sdn void mage   相信適配器這個概念對於大夥來說都不陌生了吧?曾經買過港貨的通過都知道,港貨當中的充電器插線頭是很蛋疼的三插口的,但是我們平常的充電插座,一般都是兩插口的,那麽要解決這個問題,我們的適配器就登場了

Linux 命令)Shell腳本的數組

cti err art case lin start shell pre round 1、數組定義 [[email protected] ~]# a=(1 2 3 4 5 6 7 8) [[email protected]-IDC ~]# echo $