TypeScript 裝飾器定義
前言:
裝飾器Decorator 在ECMAScript中已經(jīng)提案,但是目前還沒有定案;在TypeScript中已經(jīng)將其實(shí)現(xiàn),但是這仍是一項(xiàng)正在試驗(yàn)中的特性,如果想要使用裝飾器,需要在tsconfig.json中將experimentalDecorators屬性,將其設(shè)置為true。
1.概念
1.1定義
裝飾器是一種新的聲明,它可以作用于類聲明 、方法 、訪問器 、屬性 以及參數(shù) 上。裝飾器的使用采用@符號(hào)加一個(gè)函數(shù)名稱,例如@testDecorator,其中,這個(gè)testDecorator必須是一個(gè)函數(shù)或者 return一個(gè)函數(shù) ,這個(gè)函數(shù)在運(yùn)行的時(shí)候被調(diào)用,被裝飾的聲明作為參數(shù)會(huì)自動(dòng)傳入。
值得注意的是 ,裝飾器要緊挨著要修飾的內(nèi)容的前面 ,而且所有的裝飾器不能用在聲明文件.d.ts.中,和任何外部上下文中(比如declare)。
裝飾器的定義以及使用如下所示:
// 定義一個(gè)函數(shù)作為裝飾器函數(shù)使用
function testDecorator() {}
// 通過@符號(hào)使用裝飾器
@testDecorator
1.2裝飾器工廠
所謂的裝飾器工廠也是一個(gè)函數(shù),與普通的裝飾器函數(shù)不同的是它的返回值是一個(gè)函數(shù),返回的函數(shù)作為裝飾器調(diào)用的函數(shù)。如果使用裝飾器工廠,可以在使用的時(shí)候根據(jù)當(dāng)前的使用情況,傳遞不同的參數(shù),但是在使用的時(shí)候,就需要加上函數(shù)調(diào)用。
示例代碼如下:
// 裝飾器工廠,返回值是一個(gè)函數(shù)
function testDecorator() {
return function() {}
}
// 通過@符號(hào) + 函數(shù)調(diào)用的方式使用裝飾器
@testDecorator()
1.3裝飾器組合使用
裝飾器是可以組合使用的,也就是說可以對(duì)用一個(gè)目標(biāo),引用多個(gè)裝飾器,
示例代碼如下所示:
// 定義兩個(gè)裝飾器函數(shù)
function setName() {}
function setAge() {}
// 使用裝飾器
@setName
@setAge
class Person {}
如果使用多個(gè)裝飾器,裝飾器的執(zhí)行是有順序的,執(zhí)行順序如下:
如果使用的普通的裝飾器函數(shù)的話,執(zhí)行順序是從下往上執(zhí)行的,
示例代碼如下:
function setName(constructor: any) {
console.log('setName', constructor)
}
function setAge(constructor: any) {
console.log('setAge', constructor)
}
@setName
@setAge
class Person {}
/* 執(zhí)行結(jié)果如下:
setAge [Function: Person]
setName [Function: Person]
*/
如果是裝飾器工廠的,它的執(zhí)行順序是先從上到下依次執(zhí)行工廠函數(shù),然后從下往上依次執(zhí)行工廠函數(shù)return的函數(shù)。示例代碼如下
function setName() {
console.log('get setName')
return function (constructor: any) {
console.log('setName', constructor)
}
}
function setAge() {
console.log('get setAge')
return function (constructor: any) {
console.log('setAge', constructor)
}
}
@setName()
@setAge()
class Person {}
/* 執(zhí)行結(jié)果如下:
get setName
get setAge
setAge [Function: Person]
setName [Function: Person]
*/
1.4裝飾器求值
類的定義中不同聲明上的裝飾器將按以下規(guī)定的順序引用:
- 參數(shù)裝飾器,方法裝飾器,訪問符裝飾器或?qū)傩匝b飾器應(yīng)用到每個(gè)實(shí)例成員;
- 參數(shù)裝飾器,方法裝飾器,訪問符裝飾器或?qū)傩匝b飾器應(yīng)用到每個(gè)靜態(tài)成員;
- 參數(shù)裝飾器應(yīng)用到構(gòu)造函數(shù);
- 類裝飾器應(yīng)用到類。
2.類裝飾器
類裝飾器 在類聲明之前使用,必須緊挨著需要裝飾的內(nèi)容,類裝飾器應(yīng)用于類的聲明。
類裝飾器表達(dá)式會(huì)在運(yùn)行時(shí)當(dāng)做函數(shù)被調(diào)用,它有一個(gè)參數(shù),就是這個(gè)類的構(gòu)造函數(shù)。
示例代碼如下:
let sign = null
function setName() {
return function (constructor: Function) {
sign = constructor
}
}
@setName()
class Info {
constructor() {}
}
console.log(sign === Info) // true
console.log(sign === Info.prototype.constructor) // true
如上代碼可以知道類Info的原型對(duì)象的constructor屬性指向的其實(shí)就是Info本身。
我們還可以通過裝飾器來修改類的原型對(duì)象和構(gòu)造函數(shù),示例代碼如下:
// * 通過裝飾器 修改原型對(duì)象與構(gòu)造函數(shù)
function addName(constructor: { new (): any }) {
constructor.prototype.name = '一碗周'
}
@addName
class Person {}
const person = new Person()
console.log(person.name) // error 類型“A”上不存在屬性“name”
在上面的代碼中,我們通過addName修飾符在類Person的原型上添加一個(gè)name屬性,這樣使得通過Person類實(shí)例化的對(duì)象,都可以訪問name這個(gè)屬性,但是實(shí)際上并不是這樣的,這里已經(jīng)拋出一個(gè)異常,想要解決這個(gè)問題,可以通過類型斷言的方式,也可以通過定義一個(gè)同名接口,通過聲明合并的方式解決這個(gè)問題。
示例代碼如下:
function addName(constructor: { new (): any }) {
constructor.prototype.name = '一碗周'
}
@addName
class Person {}
const person = new Person()
// 1. 類型斷言
// console.log((person as any).name) // 一碗周
// 2. 定義同名接口,聲明合并
interface Person {
name: string
}
console.log(person.name) // 一碗周
而且我們還可以通過裝飾器重載構(gòu)造函數(shù),示例代碼如下:
// * 重載構(gòu)造函數(shù)
function classDecorator<T extends { new (...args: any[]): {} }>(
constructor: T,
) {
return class extends constructor {
name = '一碗周'
hobby = 'coding'
}
}
@classDecorator
class Person {
age = 18
name: string
constructor(name: string) {
this.name = name
}
}
const person = new Person('一碗周')
console.log(person)
/* 執(zhí)行結(jié)果如下:
{
age: 18,
name: '一碗周',
hobby: 'coding',
}
*/
我們還可以通過裝飾器工廠的方式來傳遞參數(shù),示例代碼如下:
// 定義一個(gè)裝飾器工廠
function classDecorator(_name: string) {
return function <T extends { new (...args: any[]): {} }>(constructor: T) {
return class extends constructor {
name = _name
hobby = 'coding'
}
}
}
@classDecorator('一碗周')
class Person {
age = 18
name: string
constructor(name: string) {
this.name = name
}
}
const person = new Person('一碗粥')
console.log(person)
/* 執(zhí)行結(jié)果如下:
{
age: 18,
name: '一碗周',
hobby: 'coding',
}
*/
3.方法裝飾器
方法裝飾器用來處理類中的方法,它可以處理方法的屬性描述符(關(guān)于什么是屬性描述符,請(qǐng)參考Object.defineProperty() ),也可以處理方法定義。方法裝飾器在運(yùn)行時(shí)也是被當(dāng)做函數(shù)調(diào)用,其包含三個(gè)參數(shù),
具體如下所示:
對(duì)于靜態(tài)成員來說是類的構(gòu)造函數(shù),對(duì)于實(shí)例成員是類的原型對(duì)象。
成員的名字。
成員的屬性描述符 。
值得注意的是如果代碼輸出目標(biāo)版本小于ES5,屬性描述符 將會(huì)是undefined。
如下代碼通過裝飾器工廠定義了一個(gè)簡(jiǎn)單的方法裝飾器,示例代碼如下:
// 裝飾器工廠
function enumerable(bool: boolean) {
/**
* 方法裝飾器接受三個(gè)參數(shù):
* 1. target:對(duì)于靜態(tài)成員來說是類的構(gòu)造函數(shù),對(duì)于實(shí)例成員是類的原型對(duì)象
* 2. propertyName:成員的名字
* 3. descriptor:屬性描述符,其類型為 PropertyDescriptor
*/
return function (
target: any,
propertyName: string,
descriptor: PropertyDescriptor,
) {
// 根據(jù)傳入的bool決定該方法是否可枚舉
descriptor.enumerable = bool
}
}
class Info {
constructor(public name: string) {}
@enumerable(false)
getName() {
return this.name
}
}
const info = new Info('一碗周')
// 如果直接打印,該對(duì)象中不包含 getName() 方法,因?yàn)樵摲椒ㄊ遣豢擅杜e的。
console.log(info) // { name: '一碗周' }
// 但是可以調(diào)用該方法
console.log(info.getName()) // 一碗周
在上面的代碼中,我們直接通過裝飾器對(duì)類中的方法的屬性描述符進(jìn)行了修改。
如果方法裝飾器返回一個(gè)值,那么會(huì)用這個(gè)值作為方法的屬性描述符對(duì)象,示例代碼如下:
// 裝飾器工廠
function enumerable(bool: boolean) {
return function (
target: any,
propertyName: string,
descriptor: PropertyDescriptor,
) {
return {
value: function () {
return 'Error: name is undefined'
},
enumerable: bool,
}
}
}
class Info {
constructor(public name: string) {}
@enumerable(false)
getName() {
return this.name
}
}
const info = new Info('一碗周')
console.log(info) // { name: '一碗周' }
console.log(info.getName()) // Error: name is undefined
在上面的代碼中,我們的方法裝飾器中返回了一個(gè)對(duì)象,該對(duì)象的value屬性修改了方法的定義,所以最終看到的結(jié)果為Error: name is undefined。
4.訪問器裝飾器
訪問器裝飾器就是之前所學(xué)習(xí)的set和get方法,一個(gè)在設(shè)置屬性值的時(shí)候觸發(fā),一個(gè)在獲取屬性值的時(shí)候觸發(fā)。
訪問器裝飾器同樣也接受三個(gè)參數(shù),與方法裝飾器一樣,這里不做贅述了,
示例代碼如下:
function enumerable(bool: boolean) {
return function (
target: any,
propertyName: string,
descriptor: PropertyDescriptor,
) {
descriptor.enumerable = bool
}
}
class Info {
private _name: string
constructor(name: string) {
this._name = name
}
@enumerable(false)
get name() {
return this._name
}
set name(name) {
this._name = name
}
}
值得注意的是,在TypeScript不允許同時(shí)裝飾一個(gè)成員的get和set訪問器。
5.屬性裝飾器
屬性裝飾器聲明在屬性聲明之前,它有兩個(gè)參數(shù),如下所示:
- 對(duì)于靜態(tài)成員來說是類的構(gòu)造函數(shù),對(duì)于實(shí)例成員是類的原型對(duì)象。
- 成員的名字。
示例代碼如下:
function printPropertyName(target: any, propertyName: string) {
console.log(propertyName)
}
class Info {
@printPropertyName
name: string
@printPropertyName
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
new Info('一碗周', 18)
執(zhí)行結(jié)果如下:
name
age
6.參數(shù)裝飾器
參數(shù)裝飾器具有三個(gè)參數(shù),具體如下:
- 對(duì)于靜態(tài)成員來說是類的構(gòu)造函數(shù),對(duì)于實(shí)例成員是類的原型對(duì)象。
- 成員的名字。
- 參數(shù)在函數(shù)參數(shù)列表中的索引。
參數(shù)裝飾器的作用是用于監(jiān)視一個(gè)方法的參數(shù)是否被傳入,參數(shù)裝飾器的返回值會(huì)被忽略。
示例代碼如下:
function required(target: any, propertyName: string, index: number) {
console.log(`修飾的是${propertyName}的第${index + 1}個(gè)參數(shù)`)
}
class Info {
name: string = '一碗周'
age: number = 18
getInfo(prefix: string, @required infoType: string): any {
return prefix + ' ' + this[infoType]
}
}
interface Info {
[key: string]: string | number | Function
}
const info = new Info()
info.getInfo('', 'age') // 修飾的是getInfo的第2個(gè)參數(shù)
這里我們?cè)?code>getInfo方法的第二個(gè)參數(shù)之前使用參數(shù)裝飾器,從而可以在裝飾器中獲取到一些信息。
到此這篇關(guān)于TypeScript 裝飾器定義的文章就介紹到這了,更多相關(guān)TypeScript 裝飾器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- TypeScript裝飾器的作用、結(jié)構(gòu)、類型及其應(yīng)用場(chǎng)景(代碼示例)
- 使用TypeScript和裝飾器實(shí)現(xiàn)前端數(shù)據(jù)脫敏
- TypeScript裝飾器之項(xiàng)目數(shù)據(jù)轉(zhuǎn)換詳解
- TypeScript裝飾器與反射元數(shù)據(jù)實(shí)例詳解
- TypeScript入門之利用裝飾器擴(kuò)展代碼能力
- Typescript使用裝飾器實(shí)現(xiàn)接口字段映射與Mock實(shí)例
- Typescript裝飾器AOP示例詳解
- TypeScript中的裝飾器用法
- 淺談Typescript 裝飾器執(zhí)行順序
相關(guān)文章
JavaScript 學(xué)習(xí)小結(jié)(適合新手參考)
JavaScript常量又稱字面常量,是固化在程序代碼中的信息。變量的主要作用是存取數(shù)據(jù),提供一個(gè)存取信息的容器。2009-07-07
javascript制作網(wǎng)頁圖片上實(shí)現(xiàn)下雨效果
這里給大家分享的是一則使用javascript實(shí)現(xiàn)在網(wǎng)頁圖片上下雨的特效,效果非常炫酷,推薦給小伙伴們。2015-02-02
bootstrap select2插件用ajax來獲取和顯示數(shù)據(jù)的實(shí)例
今天小編就為大家分享一篇bootstrap select2插件用ajax來獲取和顯示數(shù)據(jù)的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-08-08
使用JS代碼實(shí)現(xiàn)點(diǎn)擊按鈕下載文件
有時(shí)候我們?cè)诰W(wǎng)頁上需要增加一個(gè)下載按鈕,讓用戶能夠點(diǎn)擊后下載頁面上的資料,那么怎樣才能實(shí)現(xiàn)功能呢?今天小編給大家分享兩種方法實(shí)現(xiàn)js實(shí)現(xiàn)點(diǎn)擊按鈕下載文件,需要的朋友參考下吧2016-11-11
js字符串截取函數(shù)substr substring slice使用對(duì)比
字符串截取函數(shù)有substr、substring以及slice等等,下面將為大家介紹下各自的使用,感興趣的朋友可以了解下2013-11-11
JavaScript URL參數(shù)讀取改進(jìn)版
此前發(fā)表的那一版確實(shí)能不用循環(huán),但是總用正則表達(dá)式的替換,不一定比用循環(huán)提高性能,而且把程序搞得有些太復(fù)雜了。從《JavaScript權(quán)威指南》上學(xué)到的范例如下2009-01-01
js生成的驗(yàn)證碼的實(shí)現(xiàn)與技術(shù)分析
本文主要是分享了一段由JS生成驗(yàn)證碼并驗(yàn)證的代碼,非常簡(jiǎn)單,并分析了此方法的實(shí)用性,提供給大家參考下2014-09-09

