国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

javascript中的設計模式(二)

whidy / 604人閱讀

摘要:在類似于這樣的面向對象語言中,抽象類的使用在這個設計模式中非常重要。假設系統(tǒng)中存在大量類似的對象而導致內存消耗過高,享元模式就非常有用了。享元模式包含兩種狀態(tài)即屬性內部狀態(tài)存儲于對象內部。不過與享元模式不同的是它不會區(qū)分內部狀態(tài)和外部狀態(tài)。

模式8-模版方法模式

模版方法模式是一種基于繼承的設計模式。主要由兩部分構成:

抽象父類:包含子類的算法框架和一些通用的具體方法;

具體實現(xiàn)的子類: 包含對于父類中抽象方法的實現(xiàn),繼承父類的整個算法實現(xiàn)方法,并且可以重寫父類中的方法。

在類似于java這樣的面向對象語言中,抽象類的使用在這個設計模式中非常重要。因為在編譯的時候會對繼承抽象類的子類進行檢測,要求必須要對抽象方法進行實現(xiàn)。然而在javascript中沒有類型檢查,所以要保證子類實現(xiàn)了所有抽象父類的抽象方法,可以在運行時進行檢測,即讓抽象方法拋出錯誤。

示例:

var Beverage = function() {}
Beverage.prototype.boilWater = function(){
    console.log("boil water");
}
Beverage.prototype.brew = function (){
    throw new Error("you must define function brew");
}
Beverage.prototype.pourInCup = function (){
    throw new Error("you must define function pourInCup");
}
Beverage.prototype.addCondiments = function (){
    throw new Error("you must define function addCondiments");
}
Beverage.prototype.customerWantsCondiments = function(){
    throw new Error("you must define function customerWantsCondiments");
}

//泡飲料的順序和步驟是定的,算是一個子類通用的算法
Beverage.prototype.init = function(){ 
    this.boilWater();
    this.brew();
    this.pourInCup(); 
    if(this.customerWantsCondiments){
        this.addCondiments();
    }
};

var Tea = function(){};               
Tea.prototype = new Beverage();        
Tea.prototype.brew = function(){       
    console.log("brew-up");            
}                                     
Tea.prototype.pourInCup = function(){
    console.log("pour tea");   
}
Tea.prototype.addCondiments = function(){
    console.log("add sugar and milk");
}
Tea.prototype.customerWantsCondiments = function() {
    return window.confirm("Do you need condiments?");
}

var tea = new Tea();
tea.init();

var Coffee = function(){};               
Coffee.prototype = new Beverage();        
Coffee.prototype.brew = function(){       
    console.log("brew coffee");            
}                                     
Coffee.prototype.pourInCup = function(){
    console.log("pour coffee");   
}
Coffee.prototype.addCondiments = function(){
    console.log("add lemon");
}
Coffee.prototype.customerWantsCondiments = function() {
    return window.confirm("Do you need condiments?");
}

var coffee = new Coffee();
coffee.init();

這個設計模式是有利于系統(tǒng)的拓展的,并且符合開放-封閉原則。另外,我們在js中不一定非要使用繼承的方式來完成這個設計模式,也可以通過傳入高階函數(shù)作為參數(shù)來實現(xiàn),用以替代父類中的抽象函數(shù)。

模式9-享元模式

享元模式是為了優(yōu)化性能而存在的。假設系統(tǒng)中存在大量類似的對象而導致內存消耗過高,享元模式就非常有用了。
享元模式包含兩種狀態(tài)(即屬性):

內部狀態(tài):

-存儲于對象內部。
-可以被一些對象共享。
-獨立于具體的場景,通常不會改變。

外部狀態(tài):

取決于具體的場景,并根據(jù)場景而變化,外部狀態(tài)不能被共享。

剝離了外部狀態(tài)的對象成為共享對象,外部狀態(tài)在必要時被傳入共享對象來組裝成一個完整的對象。系統(tǒng)中可能存在的最大享元對象個數(shù)等于不同內部狀態(tài)的組合數(shù)。

示例:

//這個享元只有一個內部狀態(tài)
var Model = function( sex ){ 
    this.sex = sex;
};
Model.prototype.takePhoto = function(){
    console.log( "sex= " + this.sex + " underwear=" + this.underwear);
};

//分別創(chuàng)建一個男模特對象和一個女模特對象:
var maleModel = new Model( "male" ), 
femaleModel = new Model( "female" );

//給男模特依次穿上所有的男裝,并進行拍照:
//注:這里我們是在需要的時候才傳入外部狀態(tài)
for ( var i = 1; i <= 50; i++ ){ 
    maleModel.underwear = "underwear" + i; 
    maleModel.takePhoto();
};

//同樣,給女模特依次穿上所有的女裝,并進行拍照:
for ( var j = 1; j <= 50; j++ ){ 
    femaleModel.underwear = "underwear" + j; 
    femaleModel.takePhoto();
};

應用:
文件上傳,只根據(jù)上傳組件的不同來(使用工廠)新建uploader對象(只含有一個uploadType內部狀態(tài),同樣uploadType的被共享),而文件信息等儲存在外部,只當需要(如刪除文件)時才(通過uploadManager)將外部狀態(tài)傳入內部。

對象池是另一種性能優(yōu)化的方案,其思想是創(chuàng)建一個池子用來存放空閑對象,當需要使用該對象時從池子中取,如果沒有則創(chuàng)建,用完之后放回。不過與享元模式不同的是它不會區(qū)分內部狀態(tài)和外部狀態(tài)。

模式10-職責鏈模式

思想是將可能處理請求的對象連成一個鏈,請求者只需知道第一個對象,然后將請求沿著鏈依次傳遞直到遇到可以處理該請求的對象。這就使得請求發(fā)出著和請求接受者之間解耦,也就是說請求發(fā)出者不必知道哪個對象可以處理請求,避免了在一個函數(shù)中使用大量的if else判斷。

示例:

var handler1 = function(params){
    if(params === true) {   // check condition
        console.log("request solved by handler1");
    } else {
        return false;      // condition not valid
    }
}

var handler2 = function(params){
    if(params === true) {   // check condition
        console.log("request solved by handler2");
    } else {
        return false;      // condition not valid
    }
}

var handler3 = function(params){
    if(params === true) {   // check condition
        console.log("request solved by handler3");
    } else {
        return false;      // condition not valid
    }
}

var Chain = function( fn ){ 
    this.fn = fn;
    this.successor = null; 
};
Chain.prototype.setNextSuccessor = function( successor ){ 
    return this.successor = successor;
};
Chain.prototype.passRequest = function(){
    var ret = this.fn.apply( this, arguments );
    if ( ret === false ){
        return this.successor && this.successor.passRequest.apply( this.successor, arguments );
    }
    return ret; 
};

//調用next函數(shù)可以手動傳遞請求,用于異步處理
Chain.prototype.next= function(){
    return this.successor && this.successor.passRequest.apply( this.successor, arguments );
};


var chain1 = new Chain(handler1);
var chain2 = new Chain(handler2);
var chain3 = new Chain(handler3);

//指定節(jié)點在職責鏈中的順序
chain1.setNextSuccessor(chain2); 
chain2.setNextSuccessor(chain3);

//把請求傳遞給第一個節(jié)點:
chain1.passRequest(someParams); 

職責鏈模式使得各個鏈結點之間可以拆分重組,便于插入或刪除結點。并且請求不一定要從第一個結點開始。同時,要避免職責鏈過長帶來的性能問題。
另外,可以利用js的函數(shù)式特性將函數(shù)“鏈接”起來實現(xiàn)職責鏈模式,即為Function對象的原型添加after函數(shù)。

應用:
不同瀏覽器文件上傳控件的選擇,DOM事件冒泡等。

模式11-中介者模式

中介者模式是為了解除對象之間的強耦合關系,所有對象都通過中介者通信而不再相互引用。

示例:
假設一個網頁中幾個DOM元素的值共同決定一個按鈕的有效性,
例如input必須有效,select1和select2必須選擇

傳統(tǒng)的方法中,我們需要為這三個DOM元素添加值改變監(jiān)聽函數(shù),并且在每個監(jiān)聽函數(shù)中都要堅持另外兩個DOM的值。這時,如果我們需要加入一個input2,就需要將所有其他DOM元素的監(jiān)聽函數(shù)進行修改。
如果使用中介者模式,我們可以創(chuàng)建一個mediator對象,并且提供一個向其發(fā)送消息的借口。然后,我們?yōu)槠渌鸇OM創(chuàng)建的監(jiān)聽函數(shù)中只需要向中介者發(fā)送一個消息并且把this作為參數(shù)傳遞告訴中介者是誰發(fā)的消息。而中介者的實現(xiàn)中,只需要對收到的不同消息進行處理即可,如果需要添加新的關聯(lián)DOM,也只需要稍稍修改mediator的代碼而不會需要修改其他DOM的監(jiān)聽函數(shù)了。

總之,中介者模式使對象之間的網狀引用關系變成了一對多的關系,滿足一個對象盡可能少地了解其他對象的原則。但是該模式引入了中介者對象,也會造成一些內存消耗。

模式12-裝飾者模式

裝飾者模式用于動態(tài)地給對象增加職責。

為了保證在執(zhí)行原函數(shù)和this指向發(fā)生改變,我們需要引入代理函數(shù)。代理函數(shù)的功能是在原函數(shù)執(zhí)行之前或之后,執(zhí)行另外的函數(shù)。

示例:

使用AOP,改變Function對象的原型

Function.prototype.before = function(beforefn){
    var _self = this;
    return function(){
        beforefn.apply(this, arguments);  //確保this指向不變
        this.apply(this, arguments);
    }
}

var func1 = function(){
    alert("1");
}

func1 = func1.before(function(){
    alert("0");
});

func1();  //先輸出0再輸出1

不改變原型的做法:

var before = function(fn, beforefn){
    return function() {
        beforefn.apply(this, arguments);
        fn.apply(this, arguments);
    }
}

func1 = before(func1, function(){alert("0")});

應用:
對于某些用戶操作(如單擊按鈕)數(shù)據(jù)統(tǒng)計上報,改變函數(shù)的arguments對象(如ajax傳數(shù)據(jù)時添加屬性),表單驗證和提交功能的分離等。

這個設計模式使得開發(fā)人員在開發(fā)框架時可以只考慮對象的穩(wěn)定和基礎功能,其他需要添加的個性功能可以被動態(tài)添加。它不同于代理模式的是,代理模式的目的是為了控制對對象的訪問或添加一些功能,而裝飾者模式則是為了為對象動態(tài)添加功能,并且通常會形成一條長長的裝飾鏈。

值得注意的是,裝飾者模式返回的是一個新的函數(shù),因此原函數(shù)上的屬性會消失。同時,裝飾鏈也疊加了函數(shù)的作用域,過長則會對性能產生影響。

模式13-狀態(tài)模式

狀態(tài)模式使得一個對象在其狀態(tài)改變時改變其行為。也就是說,在不同狀態(tài)下調用同一個名字的函數(shù)其函數(shù)功能是不同的。
在狀態(tài)模式中,一般有兩類對象:context和狀態(tài)類。context構造函數(shù)中應該實例化所有的狀態(tài)類并作為context的屬性,以便context調用狀態(tài)類中的方法。而狀態(tài)類的構造函數(shù)應該以context作為參數(shù),以便可以調用context中改變其state值的接口來對其進行賦值。

例如一個擁有不同光強度的燈的控制代碼示例:

var Light = function(){
    this.offLightState = new OffLightState( this );  // 持有狀態(tài)對象的引用 
    this.weakLightState = new WeakLightState( this );
    this.strongLightState = new StrongLightState( this ); 
    this.superStrongLightState = new SuperStrongLightState( this ); 
    this.button = null;
};

Light.prototype.init = function(){
    var button = document.createElement( "button" ),
    self = this;
    this.button = document.body.appendChild( button ); 
    this.button.innerHTML = "開關";
    this.currState = this.offLightState;
    this.button.onclick = function(){ 
        self.currState.buttonWasPressed();
    } };

//接下來就要定義各種狀態(tài)類
var OffLightState = function( light ){ 
    this.light = light;
};
OffLightState.prototype.buttonWasPressed = function(){ 
    console.log( "弱光" );
    this.light.setState( this.light.weakLightState );
};

// 其他狀態(tài)類似,都需要定義一個buttonWasPressed方法,略

該設計模式優(yōu)點是將對象的狀態(tài)跟對應的方法一起封裝在一個類里,使得狀態(tài)的管理如添加刪除等更easy。狀態(tài)模式與策略模式很相似,都是有context和一些策略/狀態(tài)類。不同點是策略類之間是平行的,用戶需要知道他們的不同來選擇調用,而狀態(tài)類之間的狀態(tài)轉換關系是早就規(guī)定好的,用戶也不必知道其內部不同點。

JavaScript版本的狀態(tài)機(在js中狀態(tài)類不一定需要通過類來創(chuàng)建,可以使用顯式的對象):

var Light = function(){
    this.currState = FSM.off;   // 設置當前狀態(tài) 
    this.button = null;
};

Light.prototype.init = function(){
    var button = document.createElement( "button" ),
    self = this;
    button.innerHTML = "已關燈";
    this.button = document.body.appendChild( button );
    this.button.onclick = function(){ 
        self.currState.buttonWasPressed.call( self );
    }
};

var FSM = { 
    off: {
        buttonWasPressed: function(){
        console.log( "關燈" ); 
        this.button.innerHTML = "下一次按我是開燈"; 
        this.currState = FSM.on;
        }
    },
    on: {
        buttonWasPressed: function(){
        console.log( "開燈" ); 
        this.button.innerHTML = "下一次按我是關燈"; 
        this.currState = FSM.off;
    };

var light = new Light(); 
light.init();

模式14-適配器模式

適配器模式的作用就是轉換不兼容的接口。

示例:

var getStudents = function(){
    //這個函數(shù)返回一個對象的數(shù)組
    var arr = [
        {"id": 0,
         "name": "LiLei"
        },
        {"id": 1,
         "name": "HanMeimei"
        }
    ];
}

var printStudents = function(fn){
    var params = fn();
    for(var i = 0; i < params.length; i++){
        console.log(params[i].name + ":" + params[i].id);
    }
}

printStudents(getStudents);

//倘若現(xiàn)在提供學生信息的函數(shù)變了,返回值類型也變了:
var getStudentsObj = function () {
    var stu = {
        "0": "LiLei",
        "1": "HanMeimei"
    };
}

//adaptor
var stuAdapter = function(oldfn){
    var newRes = {};
    var stu = oldfn();
    for(var i = 0; i < stu.length; i++) {
        newRes[stu[i].id] = stu[i].name; 
    }
    return function(){
        return newRes;
    }
}

printStudent(stuAdapter(oldfn));

裝飾者模式與代理模式的結構與適配器模式很像,都是用一個對象來包裝另一個對象,但不同點仍然是他們的意圖。適配者模式只是轉換接口,不需要知道對象的具體實現(xiàn)。

P.s. 本文總結自《JavaScript設計模式與開發(fā)實踐》,曾探著

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/81086.html

相關文章

  • JavaScript高級程序設計學習筆記(在HTML中使用JavaScript

    摘要:在中使用在中使用腳本有兩種方式一種是嵌入在中的腳本,另一種是引入外部的腳本。二者并行執(zhí)行,不會造成阻塞。字符編碼,較少使用。放置的位置將腳本放在標簽前是最佳的。小結把插入到頁面中要使用元素。延遲腳本總是按照指定它們的順序執(zhí)行。 在 HTML 中使用 JavaScript 在html中使用JavaScript腳本有兩種方式一種是嵌入在HTML中的腳本,另一種是引入外部的腳本。兩種方式都離...

    Jason_Geng 評論0 收藏0
  • Javascript 設計模式讀書筆記()——封裝,簡單的創(chuàng)建對象模式

    摘要:創(chuàng)建對象中,創(chuàng)建對象的基本模式有三種。因此,在設計構造函數(shù)時,需要進行慎重考慮。因此在中,這種問題被稱作繼承破壞封裝。靜態(tài)成員每個只有一份,直接通過類對象進行訪問。 什么是封裝 找工作時一些公司給了offer后我就想知道真正拿到手的是多少,畢竟賦稅繁重。但各種稅也好,五險一金也好我實在是弄不清楚,于是我就會在網上的一些稅后收入計算器上進行計算,只需要填寫一些基本信息,比如稅前收入,所...

    lentrue 評論0 收藏0
  • JavaScript高級程序設計》筆記:在HTML中使用Javascript

    摘要:元素向頁面中插入的主要方法就是使用元素。這個屬性的用途是表明腳本在執(zhí)行時不會影響頁面的構造。因此,在元素中設置屬性,相當于告訴瀏覽器立即下載,但延遲執(zhí)行?;祀s模式會讓的行為與包含非標準特性的相同,而標準模式則讓的行為更接近標準行為。 元素 向html頁面中插入js的主要方法就是使用元素。使用元素的方式有兩種:直接在頁面中嵌入js代碼和包含外部js文件。直接在頁面中嵌入js代碼如下: ...

    genefy 評論0 收藏0
  • JavaScript:萬惡的this拿命來(

    摘要:構造函數(shù)對于被實例化的,我們稱之為構造函數(shù),及使用關鍵字調用的,對于它們來說,會被改變,指向實例。上栗子全局賦上屬性通過關鍵字創(chuàng)建實例,改變函數(shù)內部指向注解通過這個栗子,我們可以看出,通過創(chuàng)建構造函數(shù)的實例,使得的指向改變,指向了實例本身。 用栗子說this Bug年年有,今年特別多 對于JavaScript這么靈活的語言來說,少了this怎么活! function ...

    fox_soyoung 評論0 收藏0
  • Javascript:String對象總結()

    摘要:方法始終從前向后找參數(shù)接收兩個參數(shù),第一個參數(shù)可以是一個對象或者一個字符串這個字符串不會轉換成正則表達式,第二個參數(shù)可以是一個字符串或者一個函數(shù)。要想替換所有子字符串,唯一的辦法就是提供一個正則表達式,而且要指定全局標志標志。 字符串的模式匹配方法 match() 參數(shù):只接受一個參數(shù),要么是一個正則表達式,要么是一個RegExp()對象。 返回:數(shù)組。數(shù)組中的第一項是與整個模式匹配的...

    zzir 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<