第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

TypeScript 裝飾器(Decorator)

裝飾器是一種特殊類型的聲明,它能夠附加到類聲明、方法、訪問符、屬性、類方法的參數(shù)上,以達(dá)到擴(kuò)展類的行為。

自從 ES2015 引入 class,當(dāng)我們需要在多個不同的類之間共享或者擴(kuò)展一些方法或行為的時候,代碼會變得錯綜復(fù)雜,極其不優(yōu)雅,這也是裝飾器被提出的一個很重要的原因。

1. 慕課解釋

常見的裝飾器有:類裝飾器、屬性裝飾器、方法裝飾器、參數(shù)裝飾器。

裝飾器的寫法:普通裝飾器(無法傳參)、 裝飾器工廠(可傳參)。

裝飾器是一項(xiàng)實(shí)驗(yàn)性特性,在未來的版本中可能會發(fā)生改變。

若要啟用實(shí)驗(yàn)性的裝飾器特性,你必須在命令行或 tsconfig.json 里啟用 experimentalDecorators 編譯器選項(xiàng):

命令行:

tsc --target ES5 --experimentalDecorators

tsconfig.json:

{
    "compilerOptions": {
        "target": "ES5",
        "experimentalDecorators": true
    }
}                                                                          

2. 裝飾器的使用方法

裝飾器允許你在類和方法定義的時候去注釋或者修改它。裝飾器是一個作用于函數(shù)的表達(dá)式,它接收三個參數(shù) target、 namedescriptor ,然后可選性的返回被裝飾之后的 descriptor 對象。

裝飾器使用 @expression 這種語法糖形式,expression 表達(dá)式求值后必須為一個函數(shù),它會在運(yùn)行時被調(diào)用,被裝飾的聲明信息做為參數(shù)傳入。

2.1 裝飾器工廠

裝飾器工廠就是一個簡單的函數(shù),它返回一個表達(dá)式,以供裝飾器在運(yùn)行時調(diào)用。

通過裝飾器工廠方法,可以額外傳參,普通裝飾器無法傳參。

function log(param: string) {
  return function (target: any, name: string, descriptor: PropertyDescriptor) {
    console.log('target:', target)
    console.log('name:', name)
    console.log('descriptor:', descriptor)

    console.log('param:', param)
  }
}

class Employee {

  @log('with param')
  routine() {
    console.log('Daily routine')
  }
}

const e = new Employee()
e.routine()

代碼解釋:

第 1 行,聲明的 log() 函數(shù)就是一個裝飾器函數(shù),通過裝飾器工廠這種寫法,可以接收參數(shù)。

來看代碼的打印結(jié)果:

target: Employee { routine: [Function] }
name: routine
descriptor: {
  value: [Function],
  writable: true,
  enumerable: true,
  configurable: true
}
param: with param
Daily routine

可以看到,先執(zhí)行裝飾器函數(shù),然后執(zhí)行 routine() 函數(shù)。至于類屬性裝飾器函數(shù)表達(dá)式的三個參數(shù) targetnamedescriptor 之后會單獨(dú)介紹。

2.2 裝飾器組合

多個裝飾器可以同時應(yīng)用到一個聲明上,就像下面的示例:

  • 書寫在同一行上:
@f @g x
  • 書寫在多行上:
@f
@g
x

在 TypeScript 里,當(dāng)多個裝飾器應(yīng)用在一個聲明上時會進(jìn)行如下步驟的操作:

  1. 由上至下依次對裝飾器表達(dá)式求值
  2. 求值的結(jié)果會被當(dāng)作函數(shù),由下至上依次調(diào)用

通過下面的例子來觀察它們求值的順序:

function f() {
  console.log('f(): evaluated');
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log('f(): called');
  }
}

function g() {
  console.log('g(): evaluated');
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log('g(): called');
  }
}

class C {
  @f()
  @g()
  method() {}
}

在控制臺里會打印出如下結(jié)果:

f(): evaluated
g(): evaluated
g(): called
f(): called

3. 類裝飾器

類裝飾器表達(dá)式會在運(yùn)行時當(dāng)作函數(shù)被調(diào)用,類的構(gòu)造函數(shù)作為其唯一的參數(shù)。

通過類裝飾器擴(kuò)展類的屬性和方法:

function extension<T extends { new(...args:any[]): {} }>(constructor: T) {
  // 重載構(gòu)造函數(shù)
  return class extends constructor {
    // 擴(kuò)展屬性
    public coreHour = '10:00-15:00'
    // 函數(shù)重載
    meeting() {
      console.log('重載:Daily meeting!')
    }
  }
}

@extension
class Employee {
  public name!: string
  public department!: string

  constructor(name: string, department: string) {
    this.name = name
    this.department = department
  }

  meeting() {
    console.log('Every Monday!')
  }

}

let e = new Employee('Tom', 'IT')
console.log(e) // Employee { name: 'Tom', department: 'IT', coreHour: '10:00-15:00' }
e.meeting()    // 重載:Daily meeting!

函數(shù)表達(dá)式的寫法:

const extension = (constructor: Function) => {
  constructor.prototype.coreHour = '10:00-15:00'

  constructor.prototype.meeting = () => {
    console.log('重載:Daily meeting!');
  }
}

@extension
class Employee {
  public name!: string
  public department!: string

  constructor(name: string, department: string) {
    this.name = name
    this.department = department
  }

  meeting() {
    console.log('Every Monday!')
  }

}

let e: any = new Employee('Tom', 'IT')
console.log(e.coreHour) // 10:00-15:00
e.meeting()             // 重載:Daily meeting!

代碼解釋:

以上兩種寫法,其實(shí)本質(zhì)是相同的,類裝飾器函數(shù)表達(dá)式將構(gòu)造函數(shù)作為唯一的參數(shù),主要用于擴(kuò)展類的屬性和方法。

4. 作用于類屬性的裝飾器

作用于類屬性的裝飾器表達(dá)式會在運(yùn)行時當(dāng)作函數(shù)被調(diào)用,傳入下列3個參數(shù) target、name、descriptor

  1. target: 對于靜態(tài)成員來說是類的構(gòu)造函數(shù),對于實(shí)例成員是類的原型對象
  2. name: 成員的名字
  3. descriptor: 成員的屬性描述符

如果你熟悉 Object.defineProperty,你會立刻發(fā)現(xiàn)這正是 Object.defineProperty 的三個參數(shù)。

比如通過修飾器完成一個屬性只讀功能,其實(shí)就是修改數(shù)據(jù)描述符中的 writable 的值 :

function readonly(value: boolean) {
  return function (target: any, name: string, descriptor: PropertyDescriptor) {
    descriptor.writable = value
  }
}

class Employee {
  @readonly(false)
  salary() {
    console.log('這是個秘密')
  }
}

const e = new Employee()
e.salary = () => { // Error,不可寫
  console.log('change')
}
e.salary()

解釋: 因?yàn)?readonly 裝飾器將數(shù)據(jù)描述符中的 writable 改為不可寫,所以倒數(shù)第三行報錯。

5. 方法參數(shù)裝飾器

參數(shù)裝飾器表達(dá)式會在運(yùn)行時當(dāng)作函數(shù)被調(diào)用,以使用參數(shù)裝飾器為類的原型上附加一些元數(shù)據(jù),傳入下列3個參數(shù) target、nameindex

  1. target: 對于靜態(tài)成員來說是類的構(gòu)造函數(shù),對于實(shí)例成員是類的原型對象
  2. name: 成員的名字
  3. index: 參數(shù)在函數(shù)參數(shù)列表中的索引

注意第三個參數(shù)的不同。

function log(param: string) {
  console.log(param)

  return function (target: any, name: string, index: number) {
    console.log(index)
  }
}

class Employee {

  salary(@log('IT') department: string, @log('John') name: string) {
    console.log('這是個秘密')
  }

}

可以用參數(shù)裝飾器來監(jiān)控一個方法的參數(shù)是否被傳入。

6. 裝飾器執(zhí)行順序

function extension(params: string) {
  return function (target: any) {
    console.log('類裝飾器')
  }
}

function method(params: string) {
  return function (target: any, name: string, descriptor: PropertyDescriptor) {
    console.log('方法裝飾器')
  }
}

function attribute(params: string) {
  return function (target: any, name: string) {
    console.log('屬性裝飾器')
  }
}

function argument(params: string) {
  return function (target: any, name: string, index: number) {
    console.log('參數(shù)裝飾器', index)
  }
}

@extension('類裝飾器')
class Employee{
  @attribute('屬性裝飾器')
  public name!: string

  @method('方法裝飾器')
  salary(@argument('參數(shù)裝飾器') name: string, @argument('參數(shù)裝飾器') department: string) {}
}

查看運(yùn)行結(jié)果:

屬性裝飾器
參數(shù)裝飾器 1
參數(shù)裝飾器 0
方法裝飾器
類裝飾器

7. 小結(jié)

雖然裝飾器還在草案階段,但借助 TypeScript 與 Babel(需安裝 babel-plugin-transform-decorators-legacy 插件) 這樣的工具已經(jīng)被應(yīng)用于很多基礎(chǔ)庫中,當(dāng)需要在多個不同的類之間共享或者擴(kuò)展一些方法或行為時,可以使用裝飾器簡化代碼。