最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript裝飾器從基礎(chǔ)到實(shí)戰(zhàn)教程

 更新時(shí)間:2025年11月19日 11:12:29   作者:Rysxt  
裝飾器是js中一種聲明式語法特性,用于在不修改原始代碼的情況下,動(dòng)態(tài)擴(kuò)展類、方法、屬性或參數(shù)的行為,本文將從基礎(chǔ)概念入手,逐步講解裝飾器的類型、用法、進(jìn)階技巧及實(shí)戰(zhàn)場(chǎng)景,感興趣的朋友跟隨小編一起看看吧

裝飾器(Decorator)是JavaScript中一種??聲明式??的語法特性,用于在不修改原始代碼的情況下,動(dòng)態(tài)擴(kuò)展類、方法、屬性或參數(shù)的行為。它本質(zhì)上是一個(gè)??函數(shù)??,通過@符號(hào)附加到目標(biāo)上,讓代碼更優(yōu)雅、可維護(hù)。本文將從基礎(chǔ)概念入手,逐步講解裝飾器的類型、用法、進(jìn)階技巧及實(shí)戰(zhàn)場(chǎng)景。

一、裝飾器基礎(chǔ)概念

1.1 什么是裝飾器?

裝飾器是一種??特殊函數(shù)??,接收目標(biāo)對(duì)象(類、方法、屬性等)作為參數(shù),返回修改后的目標(biāo)或新的描述符。其核心價(jià)值是??分離關(guān)注點(diǎn)??——將日志記錄、權(quán)限驗(yàn)證、性能監(jiān)控等功能從業(yè)務(wù)代碼中剝離,通過裝飾器“裝飾”到目標(biāo)上。

1.2 裝飾器的語法

裝飾器使用@符號(hào),緊跟裝飾器函數(shù)名,放置在目標(biāo)聲明前:

@decorator
class MyClass {} // 類裝飾器
class MyClass {
  @decorator
  myMethod() {} // 方法裝飾器
  @decorator
  myProperty; // 屬性裝飾器
}

等價(jià)于:

class MyClass {}
MyClass = decorator(MyClass) || MyClass; // 類裝飾器
MyClass.prototype.myMethod = decorator(MyClass.prototype, 'myMethod', Object.getOwnPropertyDescriptor(MyClass.prototype, 'myMethod')) || MyClass.prototype.myMethod; // 方法裝飾器

1.3 裝飾器的執(zhí)行時(shí)機(jī)

裝飾器在??編譯階段??執(zhí)行(ES6模塊加載時(shí)),而非運(yùn)行時(shí)。例如:

function logClass(target) {
  console.log('類裝飾器執(zhí)行'); // 編譯階段輸出
  target.prototype.timestamp = new Date();
}
@logClass
class MyClass {}
// 實(shí)例化時(shí)不會(huì)再次執(zhí)行裝飾器
const instance = new MyClass();
console.log(instance.timestamp); // 輸出編譯階段的日期

二、裝飾器的主要類型

裝飾器可分為五大類,分別作用于不同的目標(biāo):

2.1 類裝飾器

??作用??:修改類的構(gòu)造函數(shù)或原型,添加靜態(tài)屬性/方法或?qū)嵗龑傩?方法。
??參數(shù)??:類的構(gòu)造函數(shù)(target)。
??示例??:為類添加靜態(tài)屬性和實(shí)例屬性。

function addStaticProperty(staticProp, value) {
  return function(target) {
    target[staticProp] = value; // 添加靜態(tài)屬性
  };
}
function addInstanceProperty(instanceProp, initialValue) {
  return function(target) {
    target.prototype[instanceProp] = initialValue; // 添加實(shí)例屬性
  };
}
@addStaticProperty('version', '1.0.0')
@addInstanceProperty('timestamp', new Date())
class MyClass {}
console.log(MyClass.version); // 輸出: 1.0.0
const instance = new MyClass();
console.log(instance.timestamp); // 輸出: 編譯階段的日期

2.2 方法裝飾器

??作用??:修改方法的描述符(valuewritable等),實(shí)現(xiàn)日志、權(quán)限、性能監(jiān)控等功能。
??參數(shù)??:

  • target:靜態(tài)方法為類的構(gòu)造函數(shù),實(shí)例方法為類的原型;
  • propertyKey:方法名;
  • descriptor:方法描述符(包含value、writable等屬性)。
    ??示例??:記錄方法調(diào)用日志。
function logMethod(target, propertyKey, descriptor) {
  const originalMethod = descriptor.value;
  descriptor.value = function(...args) {
    console.log(`調(diào)用方法 ${propertyKey},參數(shù): ${JSON.stringify(args)}`);
    const result = originalMethod.apply(this, args);
    console.log(`方法 ${propertyKey} 返回: ${result}`);
    return result;
  };
  return descriptor;
}
class Calculator {
  @logMethod
  add(a, b) {
    return a + b;
  }
}
const calc = new Calculator();
calc.add(2, 3); 
// 輸出: 
// 調(diào)用方法 add,參數(shù): [2,3]
// 方法 add 返回: 5

2.3 屬性裝飾器

??作用??:修改屬性的描述符(如writable設(shè)為false實(shí)現(xiàn)只讀),或通過Object.defineProperty定義屬性的getter/setter。
??參數(shù)??:

  • target:靜態(tài)屬性為類的構(gòu)造函數(shù),實(shí)例屬性為類的原型;
  • propertyKey:屬性名。
    ??示例??:實(shí)現(xiàn)屬性只讀。
function readOnly(target, propertyKey) {
  Object.defineProperty(target, propertyKey, {
    writable: false, // 設(shè)為不可寫
    configurable: false // 設(shè)為不可配置(防止后續(xù)修改)
  });
}
class Person {
  @readOnly
  name = 'John';
}
const person = new Person();
person.name = 'Jane'; // 嚴(yán)格模式下報(bào)錯(cuò):Cannot assign to read only property 'name'
console.log(person.name); // 輸出: John

2.4 訪問器裝飾器

??作用??:修改屬性的getter或setter方法,實(shí)現(xiàn)數(shù)據(jù)格式化、驗(yàn)證等功能。
??參數(shù)??:與方法裝飾器相同(targetpropertyKey、descriptor)。
??示例??:將屬性值轉(zhuǎn)為大寫。

function capitalize(target, propertyKey, descriptor) {
  const originalGetter = descriptor.get;
  descriptor.get = function() {
    return originalGetter.call(this).toUpperCase(); // 轉(zhuǎn)為大寫
  };
  return descriptor;
}
class User {
  constructor(name) {
    this._name = name;
  }
  @capitalize
  get name() {
    return this._name;
  }
}
const user = new User('john');
console.log(user.name); // 輸出: JOHN

2.5 參數(shù)裝飾器

??作用??:為方法參數(shù)添加元數(shù)據(jù)(如參數(shù)名、驗(yàn)證規(guī)則),常用于框架中的依賴注入或參數(shù)校驗(yàn)。
??參數(shù)??:

  • target:類的原型(實(shí)例方法)或構(gòu)造函數(shù)(靜態(tài)方法);
  • propertyKey:方法名;
  • parameterIndex:參數(shù)的索引(從0開始)。
    ??示例??:記錄參數(shù)索引。
function logParameter(target, propertyKey, parameterIndex) {
  const existingParameters = target[propertyKey + 'Parameters'] || [];
  existingParameters.push(parameterIndex);
  target[propertyKey + 'Parameters'] = existingParameters;
}
class UserService {
  getUser(@logParameter id, @logParameter name) {
    return { id, name };
  }
}
const userService = new UserService();
userService.getUser(1, 'John');
console.log(UserService.prototype.getUserParameters); // 輸出: [0, 1]

三、裝飾器的高級(jí)用法

3.1 裝飾器工廠:傳遞參數(shù)

裝飾器本身是靜態(tài)的,若需動(dòng)態(tài)配置,可通過??裝飾器工廠??(返回裝飾器函數(shù)的函數(shù))實(shí)現(xiàn)。
??示例??:帶參數(shù)的權(quán)限裝飾器。

function checkPermission(requiredRole) {
  return function(target, propertyKey, descriptor) {
    const originalMethod = descriptor.value;
    descriptor.value = function(...args) {
      const userRole = this.userRole; // 假設(shè)實(shí)例上有userRole屬性
      if (userRole === requiredRole) {
        return originalMethod.apply(this, args);
      } else {
        throw new Error(`無權(quán)限:需要${requiredRole}角色`);
      }
    };
    return descriptor;
  };
}
class AdminPanel {
  userRole = 'admin';
  @checkPermission('admin')
  deleteUser() {
    console.log('用戶已刪除');
  }
}
const panel = new AdminPanel();
panel.deleteUser(); // 輸出: 用戶已刪除
const userPanel = new AdminPanel();
userPanel.userRole = 'user';
userPanel.deleteUser(); // 報(bào)錯(cuò): 無權(quán)限:需要admin角色

3.2 多個(gè)裝飾器的執(zhí)行順序

多個(gè)裝飾器按??從外到內(nèi)??的順序應(yīng)用(聲明順序),但??從內(nèi)到外??執(zhí)行(執(zhí)行順序)。
??示例??:

function dec1(target, propertyKey, descriptor) {
  console.log('裝飾器1應(yīng)用');
  return descriptor;
}
function dec2(target, propertyKey, descriptor) {
  console.log('裝飾器2應(yīng)用');
  return descriptor;
}
class Example {
  @dec1
  @dec2
  method() {}
}
// 輸出:
// 裝飾器2應(yīng)用
// 裝飾器1應(yīng)用

3.3 組合裝飾器:簡(jiǎn)化重復(fù)代碼

通過高階函數(shù)組合多個(gè)裝飾器,減少重復(fù)代碼。
??示例??:組合日志和性能監(jiān)控裝飾器。

function composeDecorators(...decorators) {
  return function(target, propertyKey, descriptor) {
    return decorators.reduceRight((desc, decorator) => decorator(target, propertyKey, desc), descriptor);
  };
}
function logMethod(target, propertyKey, descriptor) {
  const originalMethod = descriptor.value;
  descriptor.value = function(...args) {
    console.log(`調(diào)用方法 ${propertyKey}`);
    return originalMethod.apply(this, args);
  };
  return descriptor;
}
function measureTime(target, propertyKey, descriptor) {
  const originalMethod = descriptor.value;
  descriptor.value = function(...args) {
    const start = Date.now();
    const result = originalMethod.apply(this, args);
    console.log(`方法 ${propertyKey} 執(zhí)行時(shí)間: ${Date.now() - start}ms`);
    return result;
  };
  return descriptor;
}
const logAndMeasure = composeDecorators(logMethod, measureTime);
class DataProcessor {
  @logAndMeasure
  processData(data) {
    // 模擬耗時(shí)操作
    for (let i = 0; i < 1e6; i++) {}
    return data;
  }
}
const processor = new DataProcessor();
processor.processData([1, 2, 3]);
// 輸出:
// 調(diào)用方法 processData
// 方法 processData 執(zhí)行時(shí)間: 5ms

四、裝飾器的實(shí)戰(zhàn)場(chǎng)景

4.1 日志記錄

通過裝飾器自動(dòng)記錄方法的調(diào)用信息和返回值,無需手動(dòng)添加console.log。

function logMethod(target, propertyKey, descriptor) {
  const originalMethod = descriptor.value;
  descriptor.value = function(...args) {
    console.log(`[LOG] 調(diào)用 ${propertyKey},參數(shù): ${JSON.stringify(args)}`);
    const result = originalMethod.apply(this, args);
    console.log(`[LOG] ${propertyKey} 返回: ${result}`);
    return result;
  };
  return descriptor;
}
class LoggerExample {
  @logMethod
  greet(name) {
    return `Hello, ${name}!`;
  }
}
const logger = new LoggerExample();
logger.greet('Alice');
// 輸出:
// [LOG] 調(diào)用 greet,參數(shù): ["Alice"]
// [LOG] greet 返回: Hello, Alice!

4.2 權(quán)限驗(yàn)證

通過裝飾器在方法執(zhí)行前檢查用戶權(quán)限,避免重復(fù)編寫權(quán)限邏輯。

function hasPermission(requiredPermission) {
  return function(target, propertyKey, descriptor) {
    const originalMethod = descriptor.value;
    descriptor.value = function(...args) {
      const userPermissions = this.permissions || []; // 假設(shè)實(shí)例上有permissions屬性
      if (userPermissions.includes(requiredPermission)) {
        return originalMethod.apply(this, args);
      } else {
        throw new Error(`無權(quán)限:需要${requiredPermission}`);
      }
    };
    return descriptor;
  };
}
class DocumentManager {
  permissions = ['read'];
  @hasPermission('write')
  editDocument(content) {
    console.log('文檔已編輯');
  }
}
const manager = new DocumentManager();
manager.editDocument('新內(nèi)容'); // 報(bào)錯(cuò): 無權(quán)限:需要write
manager.permissions.push('write');
manager.editDocument('新內(nèi)容'); // 輸出: 文檔已編輯

4.3 性能監(jiān)控

通過裝飾器監(jiān)控方法的執(zhí)行時(shí)間,快速定位性能瓶頸。

function measurePerformance(target, propertyKey, descriptor) {
  const originalMethod = descriptor.value;
  descriptor.value = function(...args) {
    const start = performance.now();
    const result = originalMethod.apply(this, args);
    const end = performance.now();
    console.log(`[PERF] 方法 ${propertyKey} 執(zhí)行時(shí)間: ${end - start}ms`);
    return result;
  };
  return descriptor;
}
class HeavyCalculation {
  @measurePerformance
  calculateFactorial(n) {
    return n <= 1 ? 1 : n * this.calculateFactorial(n - 1);
  }
}
const calculator = new HeavyCalculation();
calculator.calculateFactorial(10); // 輸出: [PERF] 方法 calculateFactorial 執(zhí)行時(shí)間: 1ms

4.4 自動(dòng)綁定this

通過裝飾器自動(dòng)綁定方法的this,避免在回調(diào)中丟失this

function autobind(target, propertyKey, descriptor) {
  const originalMethod = descriptor.value;
  descriptor.value = function(...args) {
    return originalMethod.apply(this, args);
  };
  return descriptor;
}
class Button {
  constructor(text) {
    this.text = text;
    this.handleClick = this.handleClick.bind(this); // 傳統(tǒng)方式
  }
  @autobind
  handleClick() {
    console.log(`按鈕文本: ${this.text}`);
  }
}
const button = new Button('Click Me');
const clickHandler = button.handleClick;
clickHandler(); // 輸出: 按鈕文本: Click Me(無需手動(dòng)綁定)

五、注意事項(xiàng)

  1. ??裝飾器的執(zhí)行順序??:多個(gè)裝飾器按從外到內(nèi)應(yīng)用,從內(nèi)到外執(zhí)行。例如@A @B等同于A(B(target)),但執(zhí)行順序是B先于A
  2. ??不能裝飾函數(shù)??:函數(shù)存在變量提升,裝飾器可能在函數(shù)聲明前執(zhí)行,導(dǎo)致意外結(jié)果。建議使用類或箭頭函數(shù)。
  3. ??兼容性問題??:裝飾器是ES提案,需通過Babel(@babel/plugin-proposal-decorators)或TypeScript(experimentalDecorators)轉(zhuǎn)譯。
  4. ??保持單一職責(zé)??:每個(gè)裝飾器只做一件事(如日志、權(quán)限),避免復(fù)雜邏輯,提高可維護(hù)性。

六、總結(jié)

裝飾器是JavaScript中強(qiáng)大的語法糖,通過聲明式的方式擴(kuò)展類、方法、屬性的功能,讓代碼更清晰、可維護(hù)。本文從基礎(chǔ)概念講起,覆蓋了類、方法、屬性、訪問器、參數(shù)五大類裝飾器,以及裝飾器工廠、組合裝飾器等高級(jí)用法,最后結(jié)合實(shí)戰(zhàn)場(chǎng)景演示了日志、權(quán)限、性能監(jiān)控等常見應(yīng)用。掌握裝飾器,能讓你寫出更優(yōu)雅、更模塊化的代碼。

到此這篇關(guān)于JavaScript裝飾器從基礎(chǔ)到實(shí)戰(zhàn)教程的文章就介紹到這了,更多相關(guān)JavaScript裝飾器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java inputstream和outputstream使用詳解

    Java inputstream和outputstream使用詳解

    這篇文章主要介紹了Java inputstream和outputstream使用詳解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • Java按時(shí)間梯度實(shí)現(xiàn)異步回調(diào)接口的方法

    Java按時(shí)間梯度實(shí)現(xiàn)異步回調(diào)接口的方法

    這篇文章主要介紹了Java按時(shí)間梯度實(shí)現(xiàn)異步回調(diào)接口,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-08-08
  • Java 實(shí)現(xiàn)將List平均分成若干個(gè)集合

    Java 實(shí)現(xiàn)將List平均分成若干個(gè)集合

    這篇文章主要介紹了Java 實(shí)現(xiàn)將List平均分成若干個(gè)集合,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • TKMybatis的介紹和使用詳解

    TKMybatis的介紹和使用詳解

    TKMybatis 是基于 Mybatis 框架開發(fā)的一個(gè)工具,本文主要介紹了TKMybatis的介紹和使用詳解,內(nèi)部實(shí)現(xiàn)了對(duì)單表的基本數(shù)據(jù)操作,只需要簡(jiǎn)單繼承 TKMybatis 提供的接口,就能夠?qū)崿F(xiàn)無需編寫任何 sql 即能完成單表操作,感興趣的可以了解一下
    2021-12-12
  • 使用SpringBoot+EasyExcel+Vue實(shí)現(xiàn)excel表格的導(dǎo)入和導(dǎo)出詳解

    使用SpringBoot+EasyExcel+Vue實(shí)現(xiàn)excel表格的導(dǎo)入和導(dǎo)出詳解

    這篇文章主要介紹了使用SpringBoot+VUE+EasyExcel?整合導(dǎo)入導(dǎo)出數(shù)據(jù)的過程詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-08-08
  • Java反轉(zhuǎn)字符串的10種方法

    Java反轉(zhuǎn)字符串的10種方法

    這篇文章主要介紹了Java反轉(zhuǎn)字符串的10種方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,下面我們來一起學(xué)習(xí)一下吧
    2019-06-06
  • Java基于Rest?Assured自動(dòng)化測(cè)試接口詳解

    Java基于Rest?Assured自動(dòng)化測(cè)試接口詳解

    Rest Assured 是一個(gè)基于 Java 的流行的用于測(cè)試 RESTful API 的庫。這篇文章主要介紹了Java如何基于Rest?Assured實(shí)現(xiàn)自動(dòng)化測(cè)試接口,需要的可以參考一下
    2023-03-03
  • SpringBoot項(xiàng)目接收前端參數(shù)的11種方式

    SpringBoot項(xiàng)目接收前端參數(shù)的11種方式

    在前后端項(xiàng)目交互中,前端傳遞的數(shù)據(jù)可以通過HTTP請(qǐng)求發(fā)送到后端, 后端在Spring Boot中如何接收各種復(fù)雜的前端數(shù)據(jù)呢?這篇文章總結(jié)了11種在Spring Boot中接收前端數(shù)據(jù)的方式,需要的朋友可以參考下
    2024-12-12
  • Java之TreeSet和TreeMap的兩種排序方式解讀

    Java之TreeSet和TreeMap的兩種排序方式解讀

    這篇文章主要介紹了Java之TreeSet和TreeMap的兩種排序方式解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • java中判斷對(duì)象類型的3種方法舉例

    java中判斷對(duì)象類型的3種方法舉例

    在Java這種強(qiáng)類型語言中類型轉(zhuǎn)換、類型判斷是經(jīng)常遇到的,下面這篇文章主要給大家介紹了關(guān)于java中判斷對(duì)象類型的3種方法,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-01-01

最新評(píng)論

汉沽区| 阿克陶县| 福贡县| 香港| 镇赉县| 佛教| 东方市| 绍兴县| 绥芬河市| 日土县| 肇庆市| 杭锦后旗| 长阳| 灵璧县| 湘潭县| 新河县| 金门县| 安仁县| 青铜峡市| 高青县| 江西省| 乌鲁木齐市| 息烽县| 临潭县| 台州市| 东阿县| 东宁县| 滁州市| 漳平市| 肇州县| 双牌县| 淄博市| 塔河县| 平乐县| 武穴市| 上思县| 武川县| 左贡县| 揭阳市| 桑日县| 比如县|