溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

JS前端中的設(shè)計(jì)模式和使用場(chǎng)景是什么

發(fā)布時(shí)間:2022-08-10 14:10:48 來(lái)源:億速云 閱讀:128 作者:iii 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要講解了“JS前端中的設(shè)計(jì)模式和使用場(chǎng)景是什么”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“JS前端中的設(shè)計(jì)模式和使用場(chǎng)景是什么”吧!

    策略模式

    策略模式的定義是:定義一系列的算法,把它們一個(gè)個(gè)封裝起來(lái),并且使它們可以相互替換。從定義不難看出,策略模式是用來(lái)解決那些一個(gè)功能有多種方案、根據(jù)不同條件輸出不同結(jié)果且條件很多的場(chǎng)景,而這些場(chǎng)景在我們工作中也經(jīng)常遇到,接下來(lái)我將用幾個(gè)例子來(lái)展示策略模式在哪里用以及如何用。

    1.績(jī)效考核

    假如我們有這么一個(gè)需求,需要根據(jù)員工的績(jī)效考核給員工發(fā)放年終獎(jiǎng)(分為A/B/C/D四個(gè)等級(jí) 分別對(duì)應(yīng)基礎(chǔ)獎(jiǎng)金的1/2/3/4倍),我們很容易就寫(xiě)出這樣的代碼

    //level 評(píng)級(jí) basicBonus 基礎(chǔ)獎(jiǎng)金
    const computeBonus(level, basicBonus) = () => {
    	if(level === 'A') {
            return basicBonus * 1;
        } else if(level === 'B') {
            return basicBonus * 2;
        } else if(level === 'C') {
            return basicBonus * 3;
        } else if(level === 'D') {
            return basicBonus * 4;
        }
    }
    computeBonus('A', 1000);//1000

    我們發(fā)現(xiàn),以上的代碼可以輕松實(shí)現(xiàn)我們的需求,但是這些代碼存在什么問(wèn)題呢?

    • computedBonus方法十分臃腫,包含太多if-else

    • 拓展性差,后續(xù)如果想要更改評(píng)級(jí)或者規(guī)則都需要進(jìn)入該函數(shù)內(nèi)部調(diào)整。

    • 復(fù)用性差。

    那策略模式是怎么解決這些問(wèn)題的呢?我們都知道,設(shè)計(jì)模式的核心之一就是將可變的和不可變的部分抽離分裝,那我們根據(jù)這個(gè)原則來(lái)修改我們的代碼,其中可變的就是如何使用這些算法(多少個(gè)評(píng)級(jí)),不變的是算法的內(nèi)容(評(píng)級(jí)對(duì)應(yīng)的獎(jiǎng)金),下面就是改變后的代碼

    //定義策略類
    const strategies = {
        'A': function(basicBonus) {
            return basicBonus * 1;
        },
        'B': function(basicBonus) {
            return basicBonus * 2;
        },
        'C': function(basicBonus) {
            return basicBonus * 3;
        },
        'D': function(basicBonus) {
            return basicBonus * 4;
        },
    }
    //使用策略類
    const computeBonus = (level, basicBonus) {
        return strategies[level](basicBonus);
    }
    computeBouns('A', 1000);//1000

    從上面可以看出,我們將每種情況都單獨(dú)弄成一個(gè)策略,然后根據(jù)傳入評(píng)級(jí)和獎(jiǎng)金計(jì)算年終獎(jiǎng),這樣我們的computeBonus方法代碼量大大減少,也不用冗雜的if-else分支,同時(shí),如果我們想要改變規(guī)則,只需要在strategies中添加對(duì)應(yīng)的策略,增加了代碼的健壯性

    2.表單驗(yàn)證

    我們?nèi)粘5墓ぷ髦?,不可避免地需要做表單相關(guān)的業(yè)務(wù),畢竟這是前端最初始的職能之一。而表單繞不開(kāi)表單驗(yàn)證,那接下來(lái)我將帶大家看看策略模式在表單中如何使用。

    需求: 假設(shè)我們有一個(gè)登錄業(yè)務(wù),提交表單前需要做驗(yàn)證,驗(yàn)證規(guī)則如下:1.用戶名稱不能為空,2.密碼不能少于6位,3.手機(jī)格式要正確。

    我們很容易寫(xiě)出以下代碼

    const verifyForm = (formData) => {
        if(formData.userName == '') {
            console.log('用戶名不能為空');
            return false
        };
        if(formData.password.length < 6) {
            console.log('密碼長(zhǎng)度不能小于6位');
            return false;
        }
        if(( !/(^1[3|5|8][0-9]{9}$)/.test(formData.phone)) {
           console.log('手機(jī)格式錯(cuò)誤');
        	return false
           }
    }

    顯然,這樣也可以完成表單校驗(yàn)的功能,但是這樣寫(xiě)同樣存在著上面說(shuō)的問(wèn)題,接下來(lái),我們看下用策略模式如何改寫(xiě)

    //編寫(xiě)策略對(duì)象
    const strategies = {
    	isEmpty: function(value, error) {
            if(value === '' {
               return error;
               })
        },
        minLength: function(value, len, error) {
            if(value.length < len {
               return error;
               })
        },
        isPhone: function(value, error) {
            if ( !/(^1[3|5|8][0-9]{9}$)/.test( value ) ){ 
     			return errorMsg; 
     			}
        };
    }
    //接下來(lái)我們編寫(xiě)實(shí)現(xiàn)類 用于生成對(duì)應(yīng)的策略實(shí)例
    class Validator {
        controustor(cache) {
            this.cache = cache || []; //保存校驗(yàn)規(guī)則
        };
        add(dom, rule, error) {
            const arr = rule.splt(':');//分離參數(shù)
            this.cache.push(function(){ // 把校驗(yàn)的步驟用空函數(shù)包裝起來(lái),并且放入 cache 
     		const strategy = arr.shift(); // 用戶挑選的 strategy 
     		arr.unshift( dom.value ); // 把 input 的 value 添加進(jìn)參數(shù)列表
     		arr.push( errorMsg ); // 把 error 添加進(jìn)參數(shù)列表
     		return strategies[ strategy ].apply( dom, ary ); 
     	});
        };
        start() {
            for ( let i = 0, validatorFunc; validatorFunc = this.cache[ i++ ]; ){ 
     			var msg = validatorFunc(); // 開(kāi)始校驗(yàn),并取得校驗(yàn)后的返回信息
     				if ( msg ){ // 如果有確切的返回值,說(shuō)明校驗(yàn)沒(méi)有通過(guò)
     							return msg; 
     							} 
     				}
        }
    }
    //編寫(xiě)完策略對(duì)象和實(shí)例類后我們就可以看看如何使用了
    const validataFunc = function(){ 
     let validator = new Validator(); // 創(chuàng)建一個(gè) validator 對(duì)象
     /***************添加一些校驗(yàn)規(guī)則****************/ 
     validator.add( registerForm.userName, 'isNonEmpty', '用戶名不能為空' ); 
     validator.add( registerForm.password, 'minLength:6', '密碼長(zhǎng)度不能少于 6 位' ); 
     validator.add( registerForm.phoneNumber, 'isMobile', '手機(jī)號(hào)碼格式不正確' ); 
     var errorMsg = validator.start(); // 獲得校驗(yàn)結(jié)果
     return errorMsg; // 返回校驗(yàn)結(jié)果
    } 
     var registerForm = document.getElementById( 'registerForm' ); 
     registerForm.onsubmit = function(){ 
     var errorMsg = validataFunc(); // 如果 errorMsg 有確切的返回值,說(shuō)明未通過(guò)校驗(yàn)
     if ( errorMsg ){ 
     alert ( errorMsg ); 
     return false; // 阻止表單提交
     } 
    };

    這樣,我們就用策略模式將需求改好了,之后如果我們的校驗(yàn)規(guī)則改變了,修改起來(lái)也是很方便的,比如:

    validator.add( registerForm.userName, 'isNonEmpty', '用戶名不能為空' ); // 改成:

    validator.add( registerForm.userName, 'minLength:10', '用戶名長(zhǎng)度不能小于 10 位' );

    而且,我們也可以給文本框添加多個(gè)校驗(yàn)規(guī)則,只需要修改下策略對(duì)象以及策略方法即可!大大地增強(qiáng)了代碼地健壯性。

    策略模式的優(yōu)缺點(diǎn):

    優(yōu)點(diǎn):

    • 避免多重條件選擇語(yǔ)句(if-else

    • 具有可拓展性,可獨(dú)立抽離封裝,避免重復(fù)復(fù)制粘貼

    缺點(diǎn):

    • 增加很多策略類或者策略對(duì)象,但是這其實(shí)不算什么大缺點(diǎn)

    • 比起直接編寫(xiě)業(yè)務(wù)代碼需要思考策略對(duì)象以及其他細(xì)節(jié)

    代理模式

    代理模式是為一個(gè)對(duì)象提供一個(gè)代用品或占位符,以便控制對(duì)它的訪問(wèn)。代理模式應(yīng)該是我們?nèi)粘S玫奖容^多的設(shè)計(jì)模式了(我們?nèi)粘9ぷ髦胁恢挥X(jué)就會(huì)用到代理模式,可能你沒(méi)發(fā)現(xiàn)而已)。

    代理模式分為保護(hù)代理(用于控制不同權(quán)限的對(duì)象對(duì)目標(biāo)對(duì)象的訪問(wèn))和虛擬代理(把開(kāi)銷很大的對(duì)象或者操作延遲到真正需要的時(shí)候再去創(chuàng)建 類比引入時(shí)動(dòng)態(tài)引入)兩種,但是前端基本不用到保護(hù)代理,或者說(shuō)很難實(shí)現(xiàn)保護(hù)代理,所以大部分情況下我們用的都是虛擬代理,接下來(lái)我主要也是講虛擬代理!

    舉個(gè)例子,加入A想要給C送情書(shū),但是A沒(méi)有直接把情書(shū)交給C,而是讓B代為傳送情書(shū),那么B就是代理,他的職責(zé)就是替A做事,這個(gè)就是最簡(jiǎn)單的代理模式,接下來(lái)我們還是老樣子,邊寫(xiě)需求邊講解

    1.圖片懶加載:

    相信大家對(duì)于圖片懶加載都不陌生吧,他可以在我們加載出目標(biāo)圖片前預(yù)加載占位圖片,避免空白區(qū)域影響體驗(yàn),那我們很容易就能寫(xiě)出下面的代碼

    const lazyImage = (function() {
        let imgNode = document.createElement('img');
        document.body.appendChild(imgNode);
        let image = new Image;
        image.onload = function() {
            imgNode.src = image.src;//在這里設(shè)置圖片的真正路由
        };
        return {
            setSrc: function(src) {
                imgNode.src = '....'//預(yù)加載本地圖片;
                image.src = src
            }
        }
    })()
    lazyImage.setSrc('https://cache.yisu.com/upload/information/20220810/112/11976.jpg');//加載真正的圖片

    我們看上面的代碼,也可以完成預(yù)加載的功能,但是這樣的代碼存在著什么樣的問(wèn)題呢

    • 違反了單一職責(zé)原則,而且耦合度太高,如果后期我們不需要懶加載了,或者需要根據(jù)判斷條件判斷是否懶加載,就不得不去動(dòng)lazyImage的代碼

    接下來(lái),我們就用代理模式來(lái)改寫(xiě)一下這個(gè)例子

    const lazyImage = (function() {
        let imageNode = document.createElement('img');
        document.body.appendChild(imageNode);
        return {
            setSrc: function(src) {
                imageNode.src = src;//設(shè)置目標(biāo)src
            }
        }
    })()
    //代理函數(shù)
    const proxyImage = (function() {
        let image = new Image;
        image.onload = function() {
            myImage.setSrc(this.src);
        }
        return {
            setSrc: function(src) {
                myImage.setSrc('....')//預(yù)加載本地圖片
                img.src = src
            }
        }
    })()
    proxyImage.setSrc('https://cache.yisu.com/upload/information/20220810/112/11976.jpg');//使用代理加載

    我們觀察用代理模式寫(xiě)的代碼,發(fā)現(xiàn)我們將預(yù)加載的邏輯轉(zhuǎn)移到了代理函數(shù)中,這樣有啥好處呢

    • 如果后期不需要預(yù)加載了,只需要取消代理,即將proxyImage.setSrc(...)改成lazyImage.setSrc(...)

    • 代理函數(shù)的使用方式和原函數(shù)一模一樣,使用者不需要知道代理的實(shí)現(xiàn)細(xì)節(jié)也能使用

    不知道大家有沒(méi)有發(fā)現(xiàn),代理函數(shù)和原函數(shù)有一部分相似的邏輯和操作,只是代理函數(shù)的功能更多,這其實(shí)也是代理模式的特征之一,代理函數(shù)在保證實(shí)現(xiàn)原函數(shù)的基本功能的前提下實(shí)現(xiàn)更多功能,這樣即使使用者不清楚邏輯也能直接使用,而且后期改動(dòng)成本很低,只需要改回原函數(shù)的使用即可!!

    2.緩存代理

    設(shè)想一下,如果現(xiàn)在要你寫(xiě)一個(gè)簡(jiǎn)單的求積函數(shù),你會(huì)怎么寫(xiě)

    const mult = function() {
    	let result = 1;
        for(let i = 0, len = arguments.length; i < len; i++) {
            result *= arguments[i];
        }
        return result
    }
    mult(1, 2, 3);//6

    我們來(lái)看一下上面的代碼有啥缺點(diǎn),上面的代碼雖然實(shí)現(xiàn)了求積,但是如果我們mult(1,2,3)之后再去mult(1,2,3),那么系統(tǒng)還是會(huì)再計(jì)算一遍,這樣無(wú)疑是性能浪費(fèi),那么我們就能用代理模式來(lái)改寫(xiě):

    const proxyMult = (function() {
    	let cache = {};//緩存計(jì)算結(jié)果
    	return function() {
    		const args = Array.prototype.join.call( arguments, ',');
            if(args in cache) {
                return cache[args]
            }
            return cache[args] = mult.apply(this.arguments)
    	}
    })();
    proxyMult(1,2,3);//6
    proxyMult(1, 2, 3);//輸出6 但是不會(huì)重新計(jì)算

    可以看到,我們用代理模式改寫(xiě)后避免了重復(fù)運(yùn)算的浪費(fèi),這只是一種情景,還有其他相似情景,比如我們分頁(yè)請(qǐng)求數(shù)據(jù),可以使用相似的思路,避免對(duì)同頁(yè)的數(shù)據(jù)重復(fù)請(qǐng)求,這在工作中非常有用?。?/p>

    感謝各位的閱讀,以上就是“JS前端中的設(shè)計(jì)模式和使用場(chǎng)景是什么”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)JS前端中的設(shè)計(jì)模式和使用場(chǎng)景是什么這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

    向AI問(wèn)一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    js
    AI