(精華)2020年8月2日 TypeScript 裝飾器的使用
裝飾器
裝飾器:裝飾器是一種特殊型別的宣告,它能夠被附加到類宣告,方法,屬性或引數上,可以修改類的行為。
通俗的講裝飾器就是一個函式方法,可以注入到類、方法、屬性引數上來擴充套件類、屬性、方法、引數的功能,
可以認為就是在原有程式碼外層包裝了一層處理邏輯。
裝飾器在身邊的例子隨處可見
水龍頭上邊的起泡器就是一個裝飾器,在裝上以後就會把空氣混入水流中,摻雜很多泡泡在水裡。
但是起泡器安裝與否對水龍頭本身並沒有什麼影響,即使拆掉起泡器,也會照樣工作,水龍頭的作用在於閥門的控制,至於水中摻不摻雜氣泡則不是水龍頭需要關心的。
在TypeScript中裝飾器還屬於實驗性語法,你必須在命令列或tsconfig.json裡啟用experimentalDecorators編譯器選項:
“experimentalDecorators”: true
裝飾器的寫法:普通裝飾器(無法傳參) 、 裝飾器工廠(可傳參)
裝飾器是過去幾年中js最大的成就之一,已是Es7的標準特性之一
為什麼要用裝飾器
可能有些時候,我們會對傳入引數的型別判斷、對返回值的排序、過濾,對函式新增節流、防抖或其他的功能性程式碼,基於多個類的繼承,各種各樣的與函式邏輯本身無關的、重複性的程式碼。
所以,對於裝飾器,可以簡單地理解為是非侵入式的行為修改。
使用裝飾器的一個重要規則
要用裝飾器擴充套件功能而不是覆蓋原來的功能
裝飾器的作用
讓人更加關注業務程式碼的開發,封裝功能輔助性的程式碼。重點就是讓人把焦點放在業務上,實現焦點分離。
如何定義裝飾器
裝飾器本身其實就是一個函式,理論上忽略引數的話,任何函式都可以當做裝飾器使用。
helloword.ts
function helloWord(target: any) {
// target.username = 'dddd';
console.log('hello Word!');
}
@helloWord
class HelloWordClass {
// static username:string;
}
var p1 = new HelloWordClass();
裝飾器執行時機
修飾器對類的行為的改變,是程式碼編譯時發生的(不是TypeScript編譯,而是js在執行機中編譯階段),而不是在執行時。這意味著,修飾器能在編譯階段執行程式碼。也就是說,修飾器本質就是編譯時執行的函式
裝飾器型別
常見的裝飾器有:類裝飾器、屬性裝飾器、方法裝飾器、引數裝飾器
1 類裝飾器
類裝飾器在類宣告之前被宣告(緊靠著類宣告)。 類裝飾器應用於類建構函式,可以用來監視,修改或替換類定義。
傳入一個引數:類的建構函式
1.1 普通裝飾器(無法傳參)
function logClass(target:any){
console.log(target);
// target 就是當前類
target.prototype.apiUrl='動態擴充套件的屬性';
target.prototype.run=function(){
console.log('我是一個run方法');
}
}
@logClass
class HttpClient{
constructor(){
}
getData(){
}
}
var http:any=new HttpClient();
console.log(http.apiUrl);
http.run();
如果需要向裝飾器裡傳參,可以藉助JavaScript中函式柯里化特性
1.2 類裝飾器:裝飾器工廠(可傳參)
function logClass(params:string){
// target 就是當前類 ,params是裝飾器傳過來的引數
return function(target:any){ // 這才是真正裝飾器
console.log(target);
console.log(params);
target.prototype.apiUrl=params;
}
}
// @logClass('http://ruanmou.com/api') 是一個立即執行函式, 其實返回的是 logClass裡面的閉包函式
@logClass('http://ruanmou.com/api')
class HttpClient{
constructor(){
}
getData(){
}
}
var http:any=new HttpClient();
console.log(http.apiUrl);
2. 屬性裝飾器
屬性裝飾器表示式會在執行時當作函式被呼叫,給屬性賦值
傳入下列2個引數:
1、對於靜態成員來說是類的建構函式,對於例項成員是類的原型物件。
2、成員的名字。
//類裝飾器,可以傳參
function logClass(params:string){
return function(target:any){
console.log(target);
console.log(params);
}
}
//屬性裝飾器
function logProperty(params:any){
console.log(params); //ruanmou.com
return function(target:any,attr:any){
console.log(target); //HttpClient的 原型方法
console.log(attr); // 使用裝飾器的那個屬性,url
target[attr]=params;
}
}
@logClass('xxxx')
class HttpClient {
@logProperty('ruanmou.com')
public url:any |undefined;
// @logProperty('laney')
public name:string | undefined;
constructor(str:string){
// this.name = str;
}
getData(){
console.log(this.url);
}
say(){
console.log(this.name);
}
}
var http=new HttpClient('hello');
http.getData();
3. 方法裝飾器
它會被應用到方法的屬性描述符上,可以用來監視,修改或者替換方法定義。
方法裝飾會在執行時傳入下列3個引數:
1、對於靜態成員來說是類的建構函式,對於例項成員是類的原型物件。
2、成員的名字。
3、成員的屬性描述符。
3.1 方法裝飾器一
function get(params:any){
return function(target:any,methodName:any,desc:any){
console.log(target);
console.log(methodName);
console.log(desc);
target.apiUrl='xxxx';
target.run=function(){
console.log('run');
}
}
}
class HttpClient{
public url:any |undefined;
constructor(){
}
@get('http://www.baidu.com')
getData(){
console.log(this.url);
}
}
var http:any=new HttpClient();
console.log(http.apiUrl);
http.run();
3.2 方法裝飾器二
function get(params:any){
return function(target:any,methodName:any,desc:any){
console.log(target);
console.log(methodName);
console.log(desc.value);
//修改裝飾器的方法 把裝飾器方法裡面傳入的所有引數改為string型別
//1、儲存當前的方法
var oMethod=desc.value;
desc.value=function(...args:any[]){
args=args.map((value)=>{
return String(value);
})
oMethod.apply(this,args);
}
}
}
class HttpClient{
public url:any |undefined;
constructor(){
}
@get('http://www.baidu.com')
getData(...args:any[]){
console.log(args);
console.log('我是getData裡面的方法');
}
}
var http=new HttpClient();
http.getData(123,'xxx');
4、引數裝飾器
引數裝飾器表示式會在執行時當作函式被呼叫,可以使用引數裝飾器為類的原型增加一些元素資料 ,
傳入下列3個引數:
1、對於靜態成員來說是類的建構函式,對於例項成員是類的原型物件。
2、方法的名字。
3、引數在函式引數列表中的索引。
function logParams(params: any) {
// 1、對於靜態成員來說是類的建構函式,對於例項成員是類的原型物件。
// 2、方法的名字。
// 3、引數在函式引數列表中的索引。
return function (target: any, methodName: any, paramsIndex: any) {
// console.log(params);
// console.log(target);
// console.log(methodName);
// console.log(paramsIndex);
target.apiUrl = params;
};
}
class HttpClient {
public url: any | undefined;
constructor() {}
getData(@logParams("3yteam.com") uuid: any) {
console.log(uuid);
}
}
var http: any = new HttpClient();
http.getData(123456);
console.log(http.apiUrl);
訪問器裝飾器
訪問器裝飾器應用於訪問器的屬性描述符,可用於觀察,修改或替換訪問者的定義。 訪問器裝飾器不能在宣告檔案中使用,也不能在任何其他環境上下文中使用(例如在宣告類中)
注意: TypeScript不允許為單個成員裝飾get和set訪問器。相反,該成員的所有裝飾器必須應用於按文件順序指定的第一個訪問器。這是因為裝飾器適用於屬性描述符,它結合了get和set訪問器,而不是單獨的每個宣告。
訪問器裝飾器表示式會在執行時當作函式被呼叫,傳入下列3個引數:
- 對於靜態成員來說是類的建構函式,對於例項成員是類的原型物件。
- 成員的名字。
- 成員的屬性描述符。
下面是使用了訪問器裝飾器(@configurable)的例子,應用於Point類的成員上:
function configurable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.configurable = value;
};
}
class Point {
private _x: number;
private _y: number;
constructor(x: number, y: number) {
this._x = x;
this._y = y;
}
@configurable(false)
get x() { return this._x; }
@configurable(false)
get y() { return this._y; }
}
裝飾器執行順序
屬性》方法》方法引數》類
如果有多個同樣的裝飾器,它會先執行後面的
function logClass1(params:string){
return function(target:any){
console.log('類裝飾器1')
}
}
function logClass2(params:string){
return function(target:any){
console.log('類裝飾器2')
}
}
function logAttribute1(params?:string){
return function(target:any,attrName:any){
console.log('屬性裝飾器1')
}
}
function logAttribute2(params?:string){
return function(target:any,attrName:any){
console.log('屬性裝飾器2')
}
}
function logMethod1(params?:string){
return function(target:any,attrName:any,desc:any){
console.log('方法裝飾器1')
}
}
function logMethod2(params?:string){
return function(target:any,attrName:any,desc:any){
console.log('方法裝飾器2')
}
}
function logParams1(params?:string){
return function(target:any,attrName:any,desc:any){
console.log('方法引數裝飾器1')
}
}
function logParams2(params?:string){
return function(target:any,attrName:any,desc:any){
console.log('方法引數裝飾器2')
}
}
@logClass1('http://www.3yteam.com/api')
@logClass2('xxxx')
class HttpClient{
@logAttribute1()
@logAttribute2()
public apiUrl:string | undefined;
constructor(){
}
@logMethod1()
@logMethod2()
getData(){
return true;
}
setData(@logParams1() attr1:any,@logParams2() attr2:any,){
}
}
var http:any=new HttpClient();
相關文章
- 使用 TypeScript 裝飾器裝飾你的程式碼TypeScript
- 如何使用 vue + typescript 編寫頁面 ( vuex裝飾器補充部分--store裝飾器 )VueTypeScript
- 使用 TypeScript 自定義裝飾器給類的方法增添監聽器 ListenerTypeScript
- 裝飾器的使用
- 如何使用 vue + typescript 編寫頁面 ( vuex裝飾器部分 )VueTypeScript
- 淺談TypeScript型別、介面、裝飾器TypeScript型別
- 使用 TypeScript 自定義裝飾器給類的屬性增添監聽器 ListenerTypeScript
- TypeScript 中裝飾器的理解?應用場景?TypeScript
- Vue框架TypeScript裝飾器使用指南Vue框架TypeScript
- JavaScript裝飾器的使用JavaScript
- 如何使用 vue + typescript 編寫頁面 ( 基礎裝飾器部分 )VueTypeScript
- 聊聊Typescript中的設計模式——裝飾器篇(decorators)TypeScript設計模式
- pytest fixtures裝飾器的使用
- 如何用 Decorator 裝飾你的 TypescriptTypeScript
- Python 工匠:使用裝飾器的技巧Python
- python類裝飾器的使用注意Python
- Angular @HostListener 裝飾器的使用筆記Angular筆記
- python裝飾器2:類裝飾器Python
- python中的裝飾器的使用實戰Python
- python的裝飾器Python
- python裝飾器的集中使用姿勢Python
- 使用 js 修飾器封裝 axiosJS封裝iOS
- 介面測試使用Python裝飾器Python
- Python的黑魔法@property裝飾器的使用技巧Python
- 裝飾模式-使用裝飾器來寫表單驗證外掛模式
- TypeScript 類裝飾器的一個例子和使用單步除錯搞清楚其執行原理TypeScript除錯
- Typescript + Javascript + Node.js + 設計模式 + OO + 裝飾器= Java (Spring Boot)TypeScriptJavaScriptNode.js設計模式Spring Boot
- python使用裝飾器實現的事件中心(監聽器)Python事件
- Javascript裝飾器的妙用JavaScript
- Python中的裝飾器Python
- 類裝飾器
- JS裝飾器JS
- Python 裝飾器Python
- Python裝飾器Python
- C#中的 Attribute 與 Python/TypeScript 中的裝飾器是同個東西嗎C#PythonTypeScript
- vscode使用ES6裝飾器器語法VSCode
- python的裝飾器@的用法Python
- 裝飾器的作用及使用方法是什麼?