摘要:裝飾者要實(shí)現(xiàn)這些相同的方法繼承自裝飾器對(duì)象創(chuàng)建具體的裝飾器,也是接收作對(duì)參數(shù)接下來我們要為每一個(gè)功能創(chuàng)建一個(gè)裝飾者對(duì)象,重寫父級(jí)方法,添加我們想要的功能。
裝飾模式
僅僅包裝現(xiàn)有的模塊,使之 “更加華麗” ,并不會(huì)影響原有接口的功能 —— 好比你給手機(jī)添加一個(gè)外殼罷了,并不影響手機(jī)原有的通話、充電等功能;使用 ES7 的 decorator
ES7 中增加了一個(gè) decorator 屬性,它借鑒自 Python
下面我們以 鋼鐵俠 為例講解如何使用 ES7 的 decorator。
以鋼鐵俠為例,鋼鐵俠本質(zhì)是一個(gè)人,只是“裝飾”了很多武器方才變得那么 NB,不過再怎么裝飾他還是一個(gè)人。
我們的示例場景是這樣的
首先創(chuàng)建一個(gè)普通的Man類,它的抵御值 2,攻擊力為 3,血量為 3;
然后我們讓其帶上鋼鐵俠的盔甲,這樣他的抵御力增加 100,變成 102;
讓其帶上光束手套,攻擊力增加 50,變成 53;
最后讓他增加“飛行”能力
【Demo 1】對(duì)方法的裝飾:裝備盔甲創(chuàng)建 Man 類:
class Man{ constructor(def = 2,atk = 3,hp = 3){ this.init(def,atk,hp); } init(def,atk,hp){ this.def = def; // 防御值 this.atk = atk; // 攻擊力 this.hp = hp; // 血量 } toString(){ return `防御力:${this.def},攻擊力:${this.atk},血量:${this.hp}`; } } var tony = new Man(); console.log(`當(dāng)前狀態(tài) ===> ${tony}`); // 輸出:當(dāng)前狀態(tài) ===> 防御力:2,攻擊力:3,血量:3
代碼直接放在 http://babeljs.io/repl/ 中運(yùn)行查看結(jié)果,
記得勾選Setting的Evaluate選項(xiàng),和 options的選項(xiàng)為legacy
創(chuàng)建 decorateArmour 方法,為鋼鐵俠裝配盔甲——注意 decorateArmour 是裝飾在方法init上的。
function decorateArmour(target, key, descriptor) { const method = descriptor.value; let moreDef = 100; let ret; descriptor.value = (...args)=>{ args[0] += moreDef; ret = method.apply(target, args); return ret; } return descriptor; } class Man{ constructor(def = 2,atk = 3,hp = 3){ this.init(def,atk,hp); } @decorateArmour init(def,atk,hp){ this.def = def; // 防御值 this.atk = atk; // 攻擊力 this.hp = hp; // 血量 } toString(){ return `防御力:${this.def},攻擊力:${this.atk},血量:${this.hp}`; } } var tony = new Man(); console.log(`當(dāng)前狀態(tài) ===> ${tony}`); // 輸出:當(dāng)前狀態(tài) ===> 防御力:102,攻擊力:3,血量:3
我們先看輸出結(jié)果,防御力的確增加了 100,看來盔甲起作用了。
Decorators 的本質(zhì)是利用了 ES5 的 Object.defineProperty 屬性,這三個(gè)參數(shù)其實(shí)是和 Object.defineProperty 參數(shù)一致的
【Demo 2】裝飾器疊加:增加光束手套在上面的示例中,我們成功為 普通人 增加 “盔甲” 這個(gè)裝飾;現(xiàn)在我想再給他增加 “光束手套”,希望額外增加 50 點(diǎn)防御值。
... function decorateLight(target, key, descriptor) { const method = descriptor.value; let moreAtk = 50; let ret; descriptor.value = (...args)=>{ args[1] += moreAtk; ret = method.apply(target, args); return ret; } return descriptor; } class Man{ constructor(def = 2,atk = 3,hp = 3){ this.init(def,atk,hp); } @decorateArmour @decorateLight init(def,atk,hp){ this.def = def; // 防御值 this.atk = atk; // 攻擊力 this.hp = hp; // 血量 } ... } var tony = new Man(); console.log(`當(dāng)前狀態(tài) ===> ${tony}`); //輸出:當(dāng)前狀態(tài) ===> 防御力:102,攻擊力:53,血量:3
在這里你就能看出裝飾模式的優(yōu)勢(shì)了,它可以對(duì)某個(gè)方法進(jìn)行疊加使用,對(duì)原類的侵入性非常小,只是增加一行@decorateLight而已,可以方便地增刪;(同時(shí)還可以復(fù)用)
【Demo 3】對(duì)類的裝飾:增加飛行能力裝飾模式有兩種:純粹的裝飾模式 和 半透明的裝飾模式。
上述的兩個(gè) demo 中所使用的應(yīng)該是 純粹的裝飾模式,它并不增加對(duì)原有類的接口;下面要講 demo 是給普通人增加“飛行”能力,相當(dāng)于給類新增一個(gè)方法,屬于 半透明的裝飾模式,有點(diǎn)兒像適配器模式的樣子。
... // 3 function addFly(canFly){ return function(target){ target.canFly = canFly; let extra = canFly ? "(技能加成:飛行能力)" : ""; let method = target.prototype.toString; target.prototype.toString = (...args)=>{ return method.apply(target.prototype,args) + extra; } return target; } } @addFly(true) class Man{ constructor(def = 2,atk = 3,hp = 3){ this.init(def,atk,hp); } @decorateArmour @decorateLight init(def,atk,hp){ this.def = def; // 防御值 this.atk = atk; // 攻擊力 this.hp = hp; // 血量 } ... } ... console.log(`當(dāng)前狀態(tài) ===> ${tony}`); // 輸出:當(dāng)前狀態(tài) ===> 防御力:102,攻擊力:53,血量:3(技能加成:飛行能力)
作用在方法上的 decorator 接收的第一個(gè)參數(shù)(target )是類的 prototype;如果把一個(gè) decorator 作用到類上,則它的第一個(gè)參數(shù) target 是 類本身。
使用原生 JS 實(shí)現(xiàn)裝飾器模式Man 是具體的類,Decorator 是針對(duì) Man 的裝飾器基類
具體的裝飾類 DecorateArmour 典型地使用 prototype 繼承方式 繼承自 Decorator 基類;
基于 IOC(控制反轉(zhuǎn))思想 ,Decorator 是接受 Man 類,而不是自己創(chuàng)建 Man 類;
// 首先我們要?jiǎng)?chuàng)建一個(gè)基類 function Man(){ this.def = 2; this.atk = 3; this.hp = 3; } // 裝飾者也需要實(shí)現(xiàn)這些方法,遵守 Man 的接口 Man.prototype={ toString:function(){ return `防御力:${this.def},攻擊力:${this.atk},血量:${this.hp}`; } } // 創(chuàng)建裝飾器,接收 Man 對(duì)象作為參數(shù)。 var Decorator = function(man){ this.man = man; } // 裝飾者要實(shí)現(xiàn)這些相同的方法 Decorator.prototype.toString = function(){ return this.man.toString(); } // 繼承自裝飾器對(duì)象 // 創(chuàng)建具體的裝飾器,也是接收 Man 作對(duì)參數(shù) var DecorateArmour = function(man){ var moreDef = 100; man.def += moreDef; Decorator.call(this,man); } DecorateArmour.prototype = new Decorator(); // 接下來我們要為每一個(gè)功能創(chuàng)建一個(gè)裝飾者對(duì)象,重寫父級(jí)方法,添加我們想要的功能。 DecorateArmour.prototype.toString = function(){ return this.man.toString(); } // 注意這里的調(diào)用方式 // 構(gòu)造器相當(dāng)于“過濾器”,面向切面的 var tony = new Man(); tony = new DecorateArmour(tony); console.log(`當(dāng)前狀態(tài) ===> ${tony}`); // 輸出:當(dāng)前狀態(tài) ===> 防御力:102,攻擊力:3,血量:3經(jīng)典實(shí)現(xiàn):Logger
經(jīng)典應(yīng)用就是 日志系統(tǒng) 了,那么我們也用 ES7 的語法給鋼鐵俠打造一個(gè)日志系統(tǒng)吧。
/** * Created by jscon on 15/10/16. */ let log = (type) => { return (target, name, descriptor) => { const method = descriptor.value; descriptor.value = (...args) => { console.info(`(${type}) 正在執(zhí)行: ${name}(${args}) = ?`); let ret; try { ret = method.apply(target, args); console.info(`(${type}) 成功 : ${name}(${args}) => ${ret}`); } catch (error) { console.error(`(${type}) 失敗: ${name}(${args}) => ${error}`); } return ret; } } } class IronMan { @log("IronMan 自檢階段") check(){ return "檢查完畢"; } @log("IronMan 攻擊階段") attack(){ return "擊倒敵人"; } @log("IronMan 機(jī)體報(bào)錯(cuò)") error(){ throw "Something is wrong!"; } } var tony = new IronMan(); tony.check(); tony.attack(); tony.error(); // 輸出: // (IronMan 自檢階段) 正在執(zhí)行: check() = ? // (IronMan 自檢階段) 成功 : check() => 檢查完畢 // (IronMan 攻擊階段) 正在執(zhí)行: attack() = ? // (IronMan 攻擊階段) 成功 : attack() => 擊倒敵人 // (IronMan 機(jī)體報(bào)錯(cuò)) 正在執(zhí)行: error() = ? // (IronMan 機(jī)體報(bào)錯(cuò)) 失敗: error() => Something is wrong!
Logger 方法的關(guān)鍵在于:
首先使用 const method = descriptor.value; 將原有方法提取出來,保障原有方法的純凈;
在 try..catch 語句是 調(diào)用 ret = method.apply(target, args);在調(diào)用之前之后分別進(jìn)行日志匯報(bào);
最后返回 return ret; 原始的調(diào)用結(jié)果
相關(guān)庫https://github.com/jayphelps/...
vue中使用裝飾器實(shí)現(xiàn)AOP編程在JavaScript中實(shí)現(xiàn)AOP,是把一個(gè)函數(shù)“動(dòng)態(tài)織入”到另一個(gè)函數(shù)之中。
首先要構(gòu)造Function的prototype
//prototype.js Function.prototype.before = function (beforefn) { let _self = this; return function () { beforefn.apply(this, arguments); return _self.apply(this, arguments); }; }; Function.prototype.after = function (afterfn) { let _self = this; return function () { let ret = _self.apply(this, arguments); afterfn.apply(this, arguments); return ret; }; }; Function.prototype.around = function (beforefn, afterfn) { let _self = this; return function () { beforefn.apply(this, arguments); let ret = _self.apply(this, arguments); afterfn.apply(this, arguments); return ret; }; };
編輯我們的裝飾器函數(shù)
//decorator.js export const before = function (...args) { return function (target, key, descriptor) { descriptor.value = descriptor.value.before(() => { console.log(`Action-${key} 觸發(fā)埋點(diǎn)!`); }); }; }; export const after = function (...args) { return function (target, key, descriptor) { descriptor.value = descriptor.value.after(() => { console.log(`Action-${key} 觸發(fā)埋點(diǎn)!`); }); }; }; export const around = function (...args) { return function (target, key, descriptor) { descriptor.value = descriptor.value.around(() => { console.log(`Action-${key} 觸發(fā)埋點(diǎn)before!`); }, () => { console.log(`Action-${key} 觸發(fā)埋點(diǎn)after!`); }); }; };
編輯我們的vue文件
//test.vue
.babelrc文件
{ "plugins": [ ["@babel/plugin-proposal-decorators", { "legacy": true }] ] }摘自
http://www.liuweibo.cn/p/254#...
淘寶前端團(tuán)隊(duì)
https://www.jianshu.com/p/208...
今日?qǐng)D 羞羞噠文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/109784.html
摘要:裝飾者模式組成結(jié)構(gòu)抽象構(gòu)件給出抽象接口或抽象類,以規(guī)范準(zhǔn)備接收附加功能的對(duì)象。裝飾者模式圖解裝飾者模式應(yīng)用場景需要擴(kuò)展一個(gè)類的功能,或給一個(gè)類添加附加職責(zé)。裝飾者對(duì)象接受所有來自客戶端的請(qǐng)求。參考資料設(shè)計(jì)模式 一、了解裝飾者模式 1.1 什么是裝飾者模式 裝飾者模式指的是在不必改變?cè)愇募褪褂美^承的情況下,動(dòng)態(tài)地?cái)U(kuò)展一個(gè)對(duì)象的功能。它是通過創(chuàng)建一個(gè)包裝對(duì)象,也就是裝飾者來包裹真實(shí)的對(duì)...
摘要:相關(guān)設(shè)計(jì)模式裝飾者模式和代理模式裝飾者模式關(guān)注再一個(gè)對(duì)象上動(dòng)態(tài)添加方法代理模式關(guān)注再對(duì)代理對(duì)象的控制訪問,可以對(duì)客戶隱藏被代理類的信息裝飾著模式和適配器模式都叫包裝模式關(guān)于新職責(zé)適配器也可以在轉(zhuǎn)換時(shí)增加新的職責(zé),但主要目的不在此。 0x01.定義與類型 定義:裝飾模式指的是在不必改變?cè)愇募褪褂美^承的情況下,動(dòng)態(tài)地?cái)U(kuò)展一個(gè)對(duì)象的功能。它是通過創(chuàng)建一個(gè)包裝對(duì)象,也就是裝飾來包裹真實(shí)的...
摘要:裝飾者模式遵循了開閉原則,對(duì)擴(kuò)展開放,對(duì)修改關(guān)閉。但是在使用裝飾者模式的同時(shí)可能會(huì)引入大量小類,而且使用裝飾者模式除了實(shí)例化組件外,還要把組件包裝進(jìn)裝飾者,會(huì)使代碼顯得不易理解。 1. 簡介 ??裝飾者模式是一種結(jié)構(gòu)型模式,它可以動(dòng)態(tài)的將責(zé)任附加到對(duì)象上,在擴(kuò)展功能方面,它比繼承更有彈性。裝飾者模式遵循了開閉原則,對(duì)擴(kuò)展開放,對(duì)修改關(guān)閉。??雖然在裝飾者模式中也使用了繼承,但是繼承只是...
摘要:簡介代理模式和裝飾者模式是兩種常見的設(shè)計(jì)模式。這里通過構(gòu)造函數(shù)的參數(shù)將被代理對(duì)象傳入到代理中,也可以通過其它方式,如提供一個(gè)方法。下面是的代碼輸出首先依然是先創(chuàng)建一個(gè)需要被代理的對(duì)象,然后把它傳入到的構(gòu)造函數(shù)中。 簡介 代理模式和裝飾者模式是兩種常見的設(shè)計(jì)模式。代理模式是為其它對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問。在某些情況下,一個(gè)對(duì)象不適合或者不能直接引用另一個(gè)對(duì)象,而代理對(duì)象可以...
閱讀 650·2021-10-13 09:39
閱讀 1456·2021-09-09 11:53
閱讀 2649·2019-08-29 13:55
閱讀 725·2019-08-28 18:08
閱讀 2597·2019-08-26 13:54
閱讀 2411·2019-08-26 11:44
閱讀 1839·2019-08-26 11:41
閱讀 3782·2019-08-26 10:15