JS裝飾器函數使用詳解
這次帶給大家JS裝飾器函數使用詳解,JS裝飾器函數使用的注意事項有哪些,下面就是實戰案例,一起來看一下。
在ES6 中增加了對類別物件的相關定義和操作(例如class 和extends ),這使得我們在多個不同類別之間共享或擴展一些方法或行為的時候,變得併不是那麼優雅。這時候,我們需要一種更優雅的方法來幫助我們完成這些事情。
什麼是裝飾器
Python 的裝飾器
在物件導向(OOP)的設計模式中,decorator被稱為裝飾模式。 OOP的裝飾模式需要透過繼承和組合來實現,而Python除了能支援 OOP 的 decorator 外,直接從語法層次支援 decorator。
如果你熟悉 python 的話,對它一定不會陌生。那我們先來看看 python 裡的裝飾器是什麼樣子的吧:
def decorator(f): print "my decorator" return f @decorator def myfunc(): print "my function" myfunc() # my decorator # my function
這裡的 @decorator 就是我們說的裝飾器。在上面的程式碼中,我們利用裝飾器給我們的目標方法執行前打印出了一行文本,並且並沒有對原始方法做任何的修改。程式碼基本上等同於:
def decorator(f): def wrapper(): print "my decorator" return f() return wrapper def myfunc(): print "my function" myfunc = decorator(myfuc)
透過程式碼我們也不難看出,裝飾器decorator 接收一個參數,也就是我們被裝飾的目標方法,處理完擴展的內容以後再返回一個方法,供以後調用,同時也失去了對原方法對象的存取。當我們對某個應用了裝飾以後,其實就改變了被裝飾方法的入口引用,使其重新指向了裝飾器返回的方法的入口點,從而來實現我們對原函數的擴展、修改等操作。
ES7 的裝飾器
ES7 中的 decorator 同樣借鑒了這個語法糖,不過依賴 ES5 的 Object.defineProperty 方法 。
Object.defineProperty
Object.defineProperty() 方法會直接在一個物件上定義一個新屬性,或修改一個物件的現有屬性, 並傳回這個對象。
此方法允許精確新增或修改物件的屬性。透過賦值來新增的普通屬性會建立在屬性枚舉期間顯示的屬性(for...in 或 Object.keys 方法), 這些值可以被改變,也可以被刪除。這種方法允許這些額外的細節從預設值改變。預設情況下,使用 Object.defineProperty() 新增的屬性值是不可變的。
語法
Object.defineProperty(obj, prop, descriptor)
obj:要在其上定義屬性的物件。
prop:要定義或修改的屬性的名稱。
descriptor:將被定義或修改的屬性描述符。
傳回值:傳遞給函數的物件。
在ES6中,由於Symbol類型的特殊性,用Symbol類型的值來做物件的key與常規的定義或修改不同,而Object.defineProperty 是定義key為Symbol的屬性的方法之一。
屬性描述符
物件裡目前存在的屬性描述符有兩種主要形式:資料描述符和存取描述符。
資料描述子是一個具有值的屬性,該值可能是可寫的,也可能不是可寫的。
存取描述子是由 getter-setter 函數對描述的屬性。
描述子必須是這兩種形式之一;不能同時是兩者。
資料描述子和存取描述子都具有以下可選鍵值:
#configurable
當且僅當該屬性的configurable 為true 時,該屬性描述子才能夠被改變,同時該屬性也能從對應的物件上被刪除。預設為 false。
enumerable
enumerable定義了物件的屬性是否可以在 for...in 迴圈和 Object.keys() 中被列舉。
當且僅當該屬性的 enumerable 為 true 時,該屬性才能夠出現在物件的列舉屬性中。預設為 false。
資料描述符同時具有以下可選鍵值:
value
#该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。
writable
当且仅当该属性的 writable 为 true 时,value 才能被赋值运算符改变。默认为 false。
存取描述符同时具有以下可选键值:
get
一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。该方法返回值被用作属性值。默认为 undefined。
set
一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认为 undefined。
如果一个描述符不具有value,writable,get 和 set 任意一个关键字,那么它将被认为是一个数据描述符。如果一个描述符同时有(value或writable)和(get或set)关键字,将会产生一个异常。
用法
类的装饰
@testable class MyTestableClass { // ... } function testable(target) { target.isTestable = true; } MyTestableClass.isTestable // true
上面代码中,@testable 就是一个装饰器。它修改了 MyTestableClass这 个类的行为,为它加上了静态属性isTestable。testable 函数的参数 target 是 MyTestableClass 类本身。
基本上,装饰器的行为就是下面这样。
@decorator class A {} // 等同于 class A {} A = decorator(A) || A;
也就是说,装饰器是一个对类进行处理的函数。装饰器函数的第一个参数,就是所要装饰的目标类。
如果觉得一个参数不够用,可以在装饰器外面再封装一层函数。
function testable(isTestable) { return function(target) { target.isTestable = isTestable; } } @testable(true) class MyTestableClass {} MyTestableClass.isTestable // true @testable(false) class MyClass {} MyClass.isTestable // false
上面代码中,装饰器 testable 可以接受参数,这就等于可以修改装饰器的行为。
注意,装饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,装饰器能在编译阶段运行代码。也就是说,装饰器本质就是编译时执行的函数。
前面的例子是为类添加一个静态属性,如果想添加实例属性,可以通过目标类的 prototype 对象操作。
下面是另外一个例子。
// mixins.js export function mixins(...list) { return function (target) { Object.assign(target.prototype, ...list) } } // main.js import { mixins } from './mixins' const Foo = { foo() { console.log('foo') } }; @mixins(Foo) class MyClass {} let obj = new MyClass(); obj.foo() // 'foo'
上面代码通过装饰器 mixins,把Foo对象的方法添加到了 MyClass 的实例上面。
方法的装饰
装饰器不仅可以装饰类,还可以装饰类的属性。
class Person { @readonly name() { return `${this.first} ${this.last}` } }
上面代码中,装饰器 readonly 用来装饰“类”的name方法。
装饰器函数 readonly 一共可以接受三个参数。
function readonly(target, name, descriptor){ // descriptor对象原来的值如下 // { // value: specifiedFunction, // enumerable: false, // configurable: true, // writable: true // }; descriptor.writable = false; return descriptor; } readonly(Person.prototype, 'name', descriptor); // 类似于 Object.defineProperty(Person.prototype, 'name', descriptor);
装饰器第一个参数是 类的原型对象,上例是 Person.prototype,装饰器的本意是要“装饰”类的实例,但是这个时候实例还没生成,所以只能去装饰原型(这不同于类的装饰,那种情况时target参数指的是类本身);
第二个参数是 所要装饰的属性名
第三个参数是 该属性的描述对象
另外,上面代码说明,装饰器(readonly)会修改属性的 描述对象(descriptor),然后被修改的描述对象再用来定义属性。
函数方法的装饰
装饰器只能用于类和类的方法,不能用于函数,因为存在函数提升。
另一方面,如果一定要装饰函数,可以采用高阶函数的形式直接执行。
function doSomething(name) { console.log('Hello, ' + name); } function loggingDecorator(wrapped) { return function() { console.log('Starting'); const result = wrapped.apply(this, arguments); console.log('Finished'); return result; } } const wrapped = loggingDecorator(doSomething);
core-decorators.js
core-decorators.js是一个第三方模块,提供了几个常见的装饰器,通过它可以更好地理解装饰器。
@autobind
autobind 装饰器使得方法中的this对象,绑定原始对象。
@readonly
readonly 装饰器使得属性或方法不可写。
@override
override 装饰器检查子类的方法,是否正确覆盖了父类的同名方法,如果不正确会报错。
import { override } from 'core-decorators'; class Parent { speak(first, second) {} } class Child extends Parent { @override speak() {} // SyntaxError: Child#speak() does not properly override Parent#speak(first, second) } // or class Child extends Parent { @override speaks() {} // SyntaxError: No descriptor matching Child#speaks() was found on the prototype chain. // // Did you mean "speak"? }
@deprecate (别名@deprecated)
deprecate 或 deprecated 装饰器在控制台显示一条警告,表示该方法将废除。
import { deprecate } from 'core-decorators'; class Person { @deprecate facepalm() {} @deprecate('We stopped facepalming') facepalmHard() {} @deprecate('We stopped facepalming', { url: 'http://knowyourmeme.com/memes/facepalm' }) facepalmHarder() {} } let person = new Person(); person.facepalm(); // DEPRECATION Person#facepalm: This function will be removed in future versions. person.facepalmHard(); // DEPRECATION Person#facepalmHard: We stopped facepalming person.facepalmHarder(); // DEPRECATION Person#facepalmHarder: We stopped facepalming // // See http://knowyourmeme.com/memes/facepalm for more details. //
@suppressWarnings
suppressWarnings 装饰器抑制 deprecated 装饰器导致的 console.warn() 调用。但是,异步代码发出的调用除外。
使用场景
装饰器有注释的作用
@testable class Person { @readonly @nonenumerable name() { return `${this.first} ${this.last}` } }
有了装饰器,就可以改写上面的代码。装饰
@connect(mapStateToProps, mapDispatchToProps) export default class MyReactComponent extends React.Component {}
相对来说,后一种写法看上去更容易理解。
新功能提醒或权限
菜单点击时,进行事件拦截,若该菜单有新功能更新,则弹窗显示。
/** * @description 在点击时,如果有新功能提醒,则弹窗显示 * @param code 新功能的code * @returns {function(*, *, *)} */ const checkRecommandFunc = (code) => (target, property, descriptor) => { let desF = descriptor.value; descriptor.value = function (...args) { let recommandFuncModalData = SYSTEM.recommandFuncCodeMap[code]; if (recommandFuncModalData && recommandFuncModalData.id) { setTimeout(() => { this.props.dispatch({type: 'global/setRecommandFuncModalData', recommandFuncModalData}); }, 1000); } desF.apply(this, args); }; return descriptor; };
loading
在 React 项目中,我们可能需要在向后台请求数据时,页面出现 loading 动画。这个时候,你就可以使用装饰器,优雅地实现功能。
@autobind @loadingWrap(true) async handleSelect(params) { await this.props.dispatch({ type: 'product_list/setQuerypParams', querypParams: params }); }
loadingWrap 函数如下:、
export function loadingWrap(needHide) { const defaultLoading = ( <p className="toast-loading"> <Loading className="loading-icon"/> <p>加载中...</p> </p> ); return function (target, property, descriptor) { const raw = descriptor.value; descriptor.value = function (...args) { Toast.info(text || defaultLoading, 0, null, true); const res = raw.apply(this, args); if (needHide) { if (get('finally')(res)) { res.finally(() => { Toast.hide(); }); } else { Toast.hide(); } } }; return descriptor; }; }
问题:这里大家可以想想看,如果我们不希望每次请求数据时都出现 loading,而是要求只要后台请求时间大于 300ms 时,才显示loading,这里需要怎么改?
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
以上是JS裝飾器函數使用詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

CrystalDiskMark是一款適用於硬碟的小型HDD基準測試工具,可快速測量順序和隨機讀取/寫入速度。接下來就讓小編為大家介紹一下CrystalDiskMark,以及crystaldiskmark如何使用吧~一、CrystalDiskMark介紹CrystalDiskMark是一款廣泛使用的磁碟效能測試工具,用於評估機械硬碟和固態硬碟(SSD)的讀取和寫入速度和隨機I/O性能。它是一款免費的Windows應用程序,並提供用戶友好的介面和各種測試模式來評估硬碟效能的不同方面,並被廣泛用於硬體評

foobar2000是一款能隨時收聽音樂資源的軟體,各種音樂無損音質帶給你,增強版本的音樂播放器,讓你得到更全更舒適的音樂體驗,它的設計理念是將電腦端的高級音頻播放器移植到手機上,提供更便捷高效的音樂播放體驗,介面設計簡潔明了易於使用它採用了極簡的設計風格,沒有過多的裝飾和繁瑣的操作能夠快速上手,同時還支持多種皮膚和主題,根據自己的喜好進行個性化設置,打造專屬的音樂播放器支援多種音訊格式的播放,它還支援音訊增益功能根據自己的聽力情況調整音量大小,避免過大的音量對聽力造成損害。接下來就讓小編為大

在如今雲端儲存已成為我們日常生活和工作中不可或缺的一部分。百度網盤作為國內領先的雲端儲存服務之一,憑藉其強大的儲存功能、高效的傳輸速度以及便捷的操作體驗,贏得了廣大用戶的青睞。而且無論你是想要備份重要文件、分享資料,還是在線上觀看影片、聽取音樂,百度網盤都能滿足你的需求。但很多用戶可能對百度網盤app的具體使用方法還不了解,那麼這篇教學就將為大家詳細介紹百度網盤app如何使用,還有疑惑的用戶們就快來跟著本文詳細了解一下吧!百度雲網盤怎麼用:一、安裝首先,下載並安裝百度雲軟體時,請選擇自訂安裝選

MetaMask(中文也叫小狐狸錢包)是一款免費的、廣受好評的加密錢包軟體。目前,BTCC已支援綁定MetaMask錢包,綁定後可使用MetaMask錢包進行快速登錄,儲值、買幣等,且首次綁定還可獲得20USDT體驗金。在BTCCMetaMask錢包教學中,我們將詳細介紹如何註冊和使用MetaMask,以及如何在BTCC綁定並使用小狐狸錢包。 MetaMask錢包是什麼? MetaMask小狐狸錢包擁有超過3,000萬用戶,是當今最受歡迎的加密貨幣錢包之一。它可免費使用,可作為擴充功能安裝在網絡

網易郵箱,作為中國網友廣泛使用的一種電子郵箱,一直以來以其穩定、高效的服務贏得了用戶的信賴。而網易信箱大師,則是專為手機使用者打造的信箱軟體,它大大簡化了郵件的收發流程,讓我們的郵件處理變得更加便利。那麼網易信箱大師該如何使用,具體又有哪些功能呢,下文中本站小編將為大家帶來詳細的內容介紹,希望能幫助到大家!首先,您可以在手機應用程式商店搜尋並下載網易信箱大師應用程式。在應用寶或百度手機助手中搜尋“網易郵箱大師”,然後按照提示進行安裝即可。下載安裝完成後,我們打開網易郵箱帳號並進行登錄,登入介面如下圖所示

Windows作業系統是全球最受歡迎的作業系統之一,其新版本Win11備受矚目。在Win11系統中,管理員權限的取得是一個重要的操作,管理員權限可以讓使用者對系統進行更多的操作和設定。本文將詳細介紹在Win11系統中如何取得管理員權限,以及如何有效地管理權限。在Win11系統中,管理員權限分為本機管理員和網域管理員兩種。本機管理員是指具有對本機電腦的完全管理權限

OracleSQL中的除法運算詳解在OracleSQL中,除法運算是一種常見且重要的數學運算運算,用來計算兩個數相除的結果。除法在資料庫查詢中經常用到,因此了解OracleSQL中的除法運算及其用法是資料庫開發人員必備的技能之一。本文將詳細討論OracleSQL中除法運算的相關知識,並提供具體的程式碼範例供讀者參考。一、OracleSQL中的除法運算

Apple在周二推出了iOS17.4更新,為iPhone帶來了一系列新功能和修復。這次更新包含了全新的表情符號,同時歐盟用戶也能夠下載其他應用程式商店。此外,更新還加強了對iPhone安全性的控制,引入了更多的「失竊設備保護」設定選項,為用戶提供更多選擇和保障。 "iOS17.3首次引入了「失竊設備保護」功能,為用戶的敏感資料增加了額外的安全保障。當用戶不在家等熟悉地點時,該功能要求用戶首次輸入生物特徵信息,並在一小時後再次輸入資訊才能存取和更改某些數據,如修改AppleID密碼或關閉失竊設備保護功能
