亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > JS > 正文

JS裝飾器函數用法總結

2024-05-06 16:43:50
字體:
來源:轉載
供稿:網友

在 ES6 中增加了對類對象的相關定義和操作(比如 class 和 extends ),這就使得我們在多個不同類之間共享或者擴展一些方法或者行為的時候,變得并不是那么優雅。這個時候,我們就需要一種更優雅的方法來幫助我們完成這些事情。

什么是裝飾器

Python 的裝飾器

在面向對象(OOP)的設計模式中,decorator被稱為裝飾模式。OOP的裝飾模式需要通過繼承和組合來實現,而Python除了能支持 OOP 的 decorator 外,直接從語法層次支持 decorator。

如果你熟悉 python 的話,對它一定不會陌生。那么我們先來看一下 python 里的裝飾器是什么樣子的吧:

def decorator(f):  print "my decorator"  return f@decoratordef myfunc():  print "my function"myfunc()# my decorator# my function

這里的 @decorator 就是我們說的裝飾器。在上面的代碼中,我們利用裝飾器給我們的目標方法執行前打印出了一行文本,并且并沒有對原方法做任何的修改。代碼基本等同于:

def decorator(f):  def wrapper():    print "my decorator"    return f()  return wrapperdef 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)
  1. obj:要在其上定義屬性的對象。
  2. prop:要定義或修改的屬性的名稱。
  3. descriptor:將被定義或修改的屬性描述符。
  4. 返回值:被傳遞給函數的對象。

在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)關鍵字,將會產生一個異常。
用法

類的裝飾

@testableclass MyTestableClass { // ...}function testable(target) { target.isTestable = true;}MyTestableClass.isTestable // true

上面代碼中,@testable 就是一個裝飾器。它修改了 MyTestableClass這 個類的行為,為它加上了靜態屬性isTestable。testable 函數的參數 target 是 MyTestableClass 類本身。

基本上,裝飾器的行為就是下面這樣。

@decoratorclass 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.jsexport function mixins(...list) { return function (target) {  Object.assign(target.prototype, ...list) }}// main.jsimport { 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)}// orclass 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 facepalmingperson.facepalmHarder();// DEPRECATION Person#facepalmHarder: We stopped facepalming////   See http://knowyourmeme.com/memes/facepalm for more details.//

@suppressWarnings

suppressWarnings 裝飾器抑制 deprecated 裝飾器導致的 console.warn() 調用。但是,異步代碼發出的調用除外。

使用場景

裝飾器有注釋的作用

@testableclass 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 = (  <div className="toast-loading">   <Loading className="loading-icon"/>   <div>加載中...</div>  </div> ); 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,這里需要怎么改?

以上就是本次小編整理的關于JS裝飾器的相關知識點,感謝大家對VeVb武林網的支持。


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
一本色道久久综合狠狠躁篇怎么玩| 国产69精品久久久久9| 亚洲国产精久久久久久久| 国产一区二区三区四区福利| 日韩精品中文字幕在线| 97精品国产97久久久久久免费| 国产福利精品av综合导导航| 一区二区三区日韩在线| 日韩欧美国产激情| 一本色道久久综合亚洲精品小说| 精品一区二区三区三区| 国产做受69高潮| 欧美一级黑人aaaaaaa做受| 国产精品久久久久久久久久ktv| 2023亚洲男人天堂| 国产亚洲欧美aaaa| 日韩在线视频国产| 国产婷婷97碰碰久久人人蜜臀| 日韩av男人的天堂| 九九热视频这里只有精品| 国产欧美 在线欧美| 九九久久综合网站| 欧美最猛性xxxx| 国产综合视频在线观看| 中文字幕日韩av电影| 另类天堂视频在线观看| 成人激情av在线| 欧美限制级电影在线观看| 久久久精品2019中文字幕神马| 欧美激情亚洲激情| 在线精品国产成人综合| 欧美一级在线亚洲天堂| 国产精品日韩电影| 5278欧美一区二区三区| 精品伊人久久97| 亚洲国产精品久久久久| 成人精品视频99在线观看免费| 久久精品国产欧美亚洲人人爽| 欧美成人剧情片在线观看| 日韩在线播放av| 亚洲综合精品伊人久久| 久久亚洲精品国产亚洲老地址| 国产91在线播放精品91| 日韩69视频在线观看| 日韩欧美精品中文字幕| 久久久久免费视频| 欧美成在线视频| 亚洲欧美日韩精品| 精品性高朝久久久久久久| 久久偷看各类女兵18女厕嘘嘘| 日韩综合中文字幕| 国产精品一区二区三区在线播放| 精品久久久久久国产91| 成人免费福利视频| 国产视频久久久久久久| 日韩中文av在线| 欧美在线视频一区二区| 国产精品福利久久久| 中文字幕日韩欧美| 亚洲欧美日韩国产中文专区| 国产精品9999| 欧美巨乳在线观看| www日韩中文字幕在线看| 欧美国产日韩一区二区| 91成品人片a无限观看| 国产日韩欧美在线看| 久久国产精品久久国产精品| 欧美精品videosex牲欧美| 欧美与欧洲交xxxx免费观看| 欧美日本啪啪无遮挡网站| 欧美大胆在线视频| 亚洲国产欧美一区二区三区久久| 色爱av美腿丝袜综合粉嫩av| 欧美激情a在线| 88国产精品欧美一区二区三区| 精品国产自在精品国产浪潮| 日韩精品免费综合视频在线播放| 亚州成人av在线| 国产免费观看久久黄| 久久香蕉精品香蕉| 成人激情在线播放| 亚洲国产精品人久久电影| 国产精品视频99| 韩国精品美女www爽爽爽视频| 国产欧美一区二区白浆黑人| 日韩av免费看网站| 亚洲国产精品va在看黑人| 亚洲色图五月天| 欧美性生活大片免费观看网址| 国产成人综合精品在线| 亚洲一区美女视频在线观看免费| 宅男66日本亚洲欧美视频| 欧美性精品220| 欧美另类老肥妇| 日韩av日韩在线观看| 亚洲91精品在线观看| 91人人爽人人爽人人精88v| 欧美资源在线观看| 日本久久久久久久| 91成人在线观看国产| 欧美多人爱爱视频网站| 国产精品视频999| 国产午夜精品视频免费不卡69堂| 日韩欧美亚洲综合| 日韩av高清不卡| 精品亚洲一区二区三区在线观看| 国产精品久久久亚洲| 98精品国产自产在线观看| 午夜精品在线视频| 久久手机免费视频| 亚洲一区二区少妇| 视频直播国产精品| 亚洲丁香久久久| 久久免费视频在线观看| 国产丝袜精品第一页| 成人在线观看视频网站| 亚洲一级片在线看| 中文字幕日韩欧美在线视频| 久久久久久久国产精品| 欧美精品制服第一页| 精品日本美女福利在线观看| 国模视频一区二区| 日韩精品福利网站| 高清在线视频日韩欧美| 中文字幕日韩av综合精品| 最新91在线视频| 久久影视电视剧免费网站清宫辞电视| 国产一区二区三区在线播放免费观看| 不卡在线观看电视剧完整版| 亚洲精品视频在线播放| 久久久噜噜噜久久中文字免| 精品国产老师黑色丝袜高跟鞋| 俺也去精品视频在线观看| 国产精品18久久久久久首页狼| 亚洲无亚洲人成网站77777| 日韩中文有码在线视频| 日韩精品在线观看一区| 欧美日产国产成人免费图片| 久久久久久久久综合| 久久久最新网址| 欧美亚洲国产视频小说| 亚洲国产精品va在线观看黑人| 亚洲人成自拍网站| 日韩av在线看| 日韩电影大全免费观看2023年上| 超碰91人人草人人干| 国产精品久久久久久婷婷天堂| 懂色aⅴ精品一区二区三区蜜月| 91视频免费在线| 91久久综合亚洲鲁鲁五月天| 中文字幕日韩电影| 日韩免费av片在线观看| 国产精品男人爽免费视频1| 亚洲精品98久久久久久中文字幕| 欧美激情第6页| 中文字幕免费国产精品| 视频一区视频二区国产精品| 精品成人在线视频| 成人免费观看a| 亚洲大胆人体在线| 亚洲a级在线观看| 中文字幕亚洲一区二区三区| 国产精品白嫩初高中害羞小美女| 国产极品jizzhd欧美|