国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 開(kāi)發(fā) > JS > 正文

新手快速入門(mén)JavaScript裝飾者模式與AOP

2024-05-06 16:52:41
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

什么是裝飾者模式

當(dāng)我們拍了一張照片準(zhǔn)備發(fā)朋友圈時(shí),許多小伙伴會(huì)選擇給照片加上濾鏡。同一張照片、不同的濾鏡組合起來(lái)就會(huì)有不同的體驗(yàn)。這里實(shí)際上就應(yīng)用了裝飾者模式:是通過(guò)濾鏡裝飾了照片。在不改變對(duì)象(照片)的情況下動(dòng)態(tài)的為其添加功能(濾鏡)。

需要注意的是:由于 JavaScript 語(yǔ)言動(dòng)態(tài)的特性,我們很容易就能改變某個(gè)對(duì)象(JavaScript 中函數(shù)是一等公民)。但是我們要盡量避免直接改寫(xiě)某個(gè)函數(shù),這會(huì)導(dǎo)致代碼的可維護(hù)性、可擴(kuò)展性變差,甚至?xí)廴酒渌麡I(yè)務(wù)。

什么是 AOP

想必大家對(duì)"餐前洗手、飯后漱口"都不陌生。這句標(biāo)語(yǔ)其實(shí)就是 AOP 在生活中的例子:吃飯這個(gè)動(dòng)作相當(dāng)于切點(diǎn),我們可以在這個(gè)切點(diǎn)前、后插入其它如洗手等動(dòng)作。

AOP(Aspect-Oriented Programming):面向切面編程,是對(duì) OOP 的補(bǔ)充。利用AOP可以對(duì)業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,也可以隔離業(yè)務(wù)無(wú)關(guān)的功能比如日志上報(bào)、異常處理等,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高業(yè)務(wù)無(wú)關(guān)的功能的復(fù)用性,也就提高了開(kāi)發(fā)的效率。

在 JavaScript 中,我們可以通過(guò)裝飾者模式來(lái)實(shí)現(xiàn) AOP,但是兩者并不是一個(gè)維度的概念。 AOP 是一種編程范式,而裝飾者是一種設(shè)計(jì)模式。

ES3 下裝飾者的實(shí)現(xiàn)

了解了裝飾者模式和 AOP 的概念之后,我們寫(xiě)一段能夠兼容 ES3 的代碼來(lái)實(shí)現(xiàn)裝飾者模式:

// 原函數(shù)var takePhoto =function(){console.log('拍照片');}// 定義 aop 函數(shù)var after=function( fn, afterfn ){ return function(){let res = fn.apply( this, arguments ); afterfn.apply( this, arguments );return res;}}// 裝飾函數(shù)var addFilter=function(){console.log('加濾鏡');}// 用裝飾函數(shù)裝飾原函數(shù)takePhoto=after(takePhoto,addFilter);takePhoto();

這樣我們就實(shí)現(xiàn)了抽離拍照與濾鏡邏輯,如果以后需要自動(dòng)上傳功能,也可以通過(guò)aop函數(shù)after來(lái)添加。

ES5 下裝飾者的實(shí)現(xiàn)

在 ES5 中引入了Object.defineProperty,我們可以更方便的給對(duì)象添加屬性:

let takePhoto = function () {console.log('拍照片');}// 給 takePhoto 添加屬性 afterObject.defineProperty(takePhoto, 'after', {writable: true,value: function () {console.log('加濾鏡');},});// 給 takePhoto 添加屬性 beforeObject.defineProperty(takePhoto, 'before', {writable: true,value: function () {console.log('打開(kāi)相機(jī)');},});// 包裝方法let aop = function (fn) {return function () {fn.before()fn()fn.after()}}takePhoto = aop(takePhoto)takePhoto()

基于原型鏈和類(lèi)的裝飾者實(shí)現(xiàn)

我們知道,在 JavaScript 中,函數(shù)也好,類(lèi)也好都有著自己的原型,通過(guò)原型鏈我們也能夠很方便的動(dòng)態(tài)擴(kuò)展,以下是基于原型鏈的寫(xiě)法:

class Test {takePhoto() {console.log('拍照');}}// after AOPfunction after(target, action, fn) {let old = target.prototype[action];if (old) {target.prototype[action] = function () {let self = this;fn.bind(self);fn(handle);}}}// 用 AOP 函數(shù)修飾原函數(shù)after(Test, 'takePhoto', () => {console.log('添加濾鏡');});let t = new Test();t.takePhoto();

使用 ES7 修飾器實(shí)現(xiàn)裝飾者

在 ES7 中引入了@decorator 修飾器的提案,參考阮一峰的文章。修飾器是一個(gè)函數(shù),用來(lái)修改類(lèi)的行為。目前Babel轉(zhuǎn)碼器已經(jīng)支持。注意修飾器只能裝飾類(lèi)或者類(lèi)屬性、方法。三者的具體區(qū)別請(qǐng)參考 MDN Object.defineProperty ;而 TypeScript 的實(shí)現(xiàn)又有所不同:TypeScript Decorator。

接下來(lái)我們通過(guò)修飾器來(lái)實(shí)現(xiàn)對(duì)方法的裝飾:

function after(target, key, desc) {const { value } = desc;desc.value = function (...args) {let res = value.apply(this, args);console.log('加濾鏡')return res;}return desc;}class Test{@aftertakePhoto(){console.log('拍照')}}let t = new Test()t.takePhoto()

可以看到,使用修飾器的代碼非常簡(jiǎn)潔明了。

場(chǎng)景:性能上報(bào)

裝飾者模式可以應(yīng)用在很多場(chǎng)景,典型的場(chǎng)景是記錄某異步請(qǐng)求請(qǐng)求耗時(shí)的性能數(shù)據(jù)并上報(bào):

function report(target, key, desc) {const { value } = desc;desc.value = async function (...args) {let start = Date.now();let res = await value.apply(this, args);let millis = Date.now()-start;// 上報(bào)代碼return res;}return desc;}class Test{@reportgetData(url){// fetch 代碼}}let t = new Test()t.getData()

這樣使用@report修飾后的代碼就會(huì)上報(bào)請(qǐng)求所消耗的時(shí)間。擴(kuò)展或者修改report函數(shù)不會(huì)影響業(yè)務(wù)代碼,反之亦然。

場(chǎng)景:異常處理

我們可以對(duì)原有代碼進(jìn)行簡(jiǎn)單的異常處理,而無(wú)需侵入式的修改:

function handleError(target, key, desc) {const { value } = desc;desc.value = async function (...args) {let res;try{res = await value.apply(this, args);}catch(err){// 異常處理logger.error(err)}return res;}return desc;}class Test{@handleErrorgetData(url){// fetch 代碼}}let t = new Test()t.getData()

通過(guò)以上兩個(gè)示例我們可以看到,修飾器的定義很簡(jiǎn)單,功能卻非常強(qiáng)大。

小結(jié)

我們一步一步通過(guò)高階函數(shù)、原型鏈、Object.defineProperty和@Decorator分別實(shí)現(xiàn)了裝飾者模式。接下來(lái)在回顧一下:

  • 裝飾者模式非常適合給業(yè)務(wù)代碼附加非業(yè)務(wù)相關(guān)功能(如日志上報(bào)),就如同給照片加濾鏡;
  • 裝飾者模式非常適合無(wú)痛擴(kuò)展別人的代碼(你經(jīng)常需要接手別人的項(xiàng)目吧)

有些朋友可能會(huì)覺(jué)得裝飾者模式和 vue 的 mixin 機(jī)制很像,其實(shí)他們都是“開(kāi)放-封閉原則”和“單一職責(zé)原則”的體現(xiàn)。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持VeVb武林網(wǎng)。文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,


注:相關(guān)教程知識(shí)閱讀請(qǐng)移步到JavaScript/Ajax教程頻道。
發(fā)表評(píng)論 共有條評(píng)論
用戶(hù)名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 和政县| 乐山市| 深圳市| 土默特右旗| 南阳市| 文昌市| 汉源县| 平果县| 金华市| 固阳县| 前郭尔| 祥云县| 武隆县| 柳州市| 平南县| 泗阳县| 河北区| 桐城市| 忻城县| 昌黎县| 东阿县| 垫江县| 忻城县| 连平县| 札达县| 普兰县| 分宜县| 墨玉县| 铜山县| 临潭县| 武陟县| 阜宁县| 富蕴县| 涟源市| 泰来县| 仙居县| 石台县| 林州市| 武宣县| 武川县| 甘谷县|