裝飾器
特殊的類型聲明,它能夠附加到類聲明,方法,屬性或參數上,可以修改類的行爲。
通俗的講裝飾器就是一個方法,可以注入到類、方法、屬性參數上來擴展類、屬性、方法、參數的功能。
常見的裝飾器有:類裝飾器、屬性裝飾器、方法裝飾器、參數裝飾器。
裝飾器的寫法:普通裝飾器(無參)、裝飾工廠(可傳參)
裝飾器是過去幾年中js 最大的成就之一,已是ES7的標準特徵之一。
普通類裝飾器
類裝飾器是在類聲明之前被聲明(緊靠着類聲明)。類裝飾器應用於類構造函數,可以用來監視,修改或者替換類定義。
//定義裝飾器
function logClass(params:any){
console.log(params:any);
//這裏的params 就是當前類
}
@logClass //調用裝飾器
class HttpClient{
constructor(){
}
getData(){
}
}
打印結果:
那麼已知是當前類,那麼就可以操作該類,可以擴展,修改這個類。
//定義裝飾器
function logClass(params:any){
console.log(params:any);
//這裏的params 就是當前類
params.prototype.apiUrl = 'xxx';
params.prototype.run = function{
console.log("--------run---------");
}
}
@logClass //調用裝飾器
class HttpClient{
constructor(){
}
getData(){
}
}
var http:any = new HttpClient();
console.log(http.apiUrl);
http.run();
帶參數的類裝飾器(裝飾器工廠)
//定義裝飾器工廠
function logClass(params:string){
return function(target:any){
console.log(target);
console.log(params);
}
target.prototype.apiUrl = params; //屬性裝飾器
}
@logClass("www.baidu.com") //調用裝飾器
class HttpClient{
constructor(){
}
getData(){
}
}
var http:any = new HttpClient();
console.log(http.apiUrl);
通過打印可以看到,將字符串hello 賦值給了params,將對象賦值給了target.
類裝飾器重載構造函數
裝飾器表達式會在運行時當做函數被調用,類的構造函數作爲其唯一的參數。
如果類裝飾器返回一個值,他會使用提供的構造函數來替換類的聲明。
function logClass(target:any){
console.log(target);
//重載構造函數
return class extends target {
apiUrl:any = "我是修改後的數據";
}
getData(){
this.apiUrl = this.apiUrl+'----';
console.log(this.apiUrl);
}
}
@logClass
class HttpClient{
public apiUrl:string | undefined;
constructor(){
this.apiUrl = "我是構造函數裏面的apiUrl";
}
getData(){
console.log(this.apiUrl);
}
}
var http = new HttpClient();
http.getData();
屬性裝飾器
屬性裝飾表達式會在運行時當做函數被調用,傳入下列兩個參數:
- 對於靜態成員來說是類的構造函數,對於實例成員是類的原型對象。
- 成員的名字。
//類裝飾器
function logClass(params:string){
return function(target:any){
console.log(target);
console.log(target);
}
}
//屬性裝飾器
function logProperty(params:any){
return function(target:any,attr:any){
console.log(target);
console.log(attr);
target[attr] = params;
}
}
@logClass('xxx')
class HttpClient{
@logProperty("http://www.baidu.com")
public apiUrl:string | undefined;
constructor(){
}
getData(){
console.log(this.apiUrl);
}
}
屬性裝飾器 中打印的 target就是當前對象, 而attr 就是屬性 apiUrl,所以可以根據
target[attr] = params;
來修改屬性。
方法裝飾器
方法裝飾器它會被應用到方法的屬性描述符上,可以來監視,修改或者替換方法定義。
方法裝飾器會在運行時傳入下列3個參數:
1、對靜態成員來說是累的構造函數,對於實例成員是累的原型對象。
2、成員的名字。
3、成員的屬性描述符。
//方法裝飾器
function logmethod(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(this.url);
}
}
}
class HttpClient{
public apiUrl:string | undefined;
constructor(){
}
@logmethod('www.baidu.com')
getData(){
}
}
var http:any = new HttpClient();
console.log(http:apiUrl);
http.run();
同理,target 還是這個原型對象,methodName就是getData 即方法名稱, desc是個對象,對象中的value 就是方法名稱。
演示:用方法裝飾器修改方法,將方法裝飾器裏面傳入的所有參數改爲string 類型。
原理:修改desc.value
//方法裝飾器
function logmethod(params:any){
return function(target:any,methodName:any,desc:any){
console.log(target);
console.log(methodName);
console.log(desc);
// 1保存當前方法
var oldMethod = desc.value;
desc.value = function(...args:any[]){//用三點運算符來接收傳入的數組參數
args = args.map((value)=>{
return String(value);
})
console.log(args);
}
}
}
class HttpClient{
public apiUrl:string | undefined;
constructor(){
}
@logmethod('www.baidu.com')
getData(){
console.log("我是getData裏面的方法");
}
}
var http = new HttpClient();
http.getData("123","xxx");
很明顯getData方法被重寫了。
那麼如果想修改原來的getData方法那應該這麼寫
//方法裝飾器
function logmethod(params:any){
return function(target:any,methodName:any,desc:any){
console.log(target);
console.log(methodName);
console.log(desc);
// 1保存當前方法
var oldMethod = desc.value;
desc.value = function(...args:any[]){//用三點運算符來接收傳入的數組參數
args = args.map((value)=>{
return String(value);
})
console.log(args);
oldMethod.apply(this,args);
}
}
}
class HttpClient{
public apiUrl:string | undefined;
constructor(){
}
@logmethod('www.baidu.com')
getData(){
console.log("我是getData裏面的方法");
}
}
var http = new HttpClient();
http.getData("123","xxx");
這裏的 oldMethod.apply(this,args); 中的this表示
desc.value = function(...args:any[]){ } ,因爲是在當前方法中操作的,所以this 指代當前方法。
而args代表參數,它的整體含義就是,在新的方法中傳入參數然後調用原來的方法。
可以很明顯的看到,原來的getData方法被成功執行。
如果此時將...args:any[] 參數數組傳入原來的getData方法中即代碼如下
//方法裝飾器
function logmethod(params:any){
return function(target:any,methodName:any,desc:any){
console.log(target);
console.log(methodName);
console.log(desc);
// 1保存當前方法
var oldMethod = desc.value;
desc.value = function(...args:any[]){//用三點運算符來接收傳入的數組參數
args = args.map((value)=>{
return String(value);
})
console.log(args);
oldMethod.apply(this,args);
}
}
}
class HttpClient{
public apiUrl:string | undefined;
constructor(){
}
@logmethod('www.baidu.com')
getData(...args:any[]){
console.log(args:any);
console.log("我是getData裏面的方法");
}
}
var http = new HttpClient();
http.getData("123","xxx");
打印的結果如下
可以看到原來的方法中的參數全部變成了string 類型,
這就是方法裝飾器,它可以修改方法,也可以修改傳入參數類型。
方法參數裝飾器
參數裝飾器表達式會在運行時當做函數被調用,可以使用參數裝飾器爲類的原型增加一些數據,傳入下列三個參數:
1、對於靜態成員來說是類的構造函數,對於實例成員來說是類的原型對象。
2、參數的名字。
3、參數在函數參數列表中的索引。
//方法參數裝飾器
function logparams(params:any){
return function(target:any,paramsName:any,paamsIndex:any){
console.log(params);
console.log(target);
console.log(paramsName);
console.log(paamsIndex);
}
}
class HttpClient{
public apiUrl:string | undefined;
constructor(){
}
getData(@logparams('xxxxx'uuid:any)){
console.log("我是getData裏面的方法");
}
}
var http = new HttpClient();
http.getData(123456);
uuid成功被打印。
裝飾器執行順序
//定義裝飾器工廠
function logClass1(params:string){
return function(target:any){
console.log('類裝飾器1');
}
}
function logClass2(params:string){
return function(target:any){
console.log('類裝飾器2');
}
}
//屬性裝飾器
function logProperty(params?:any){
return function(target:any,attr:any){
console.log('屬性裝飾器');
}
}
//方法裝飾器
function logmethod(params:any){
return function(target:any,methodName:any,desc:any){
console.log('方法裝飾器');
}
}
//方法參數裝飾器
function logparams1(params:any){
return function(target:any,paramsName:any,paamsIndex:any){
console.log('方法參數裝飾器1');
}
}
function logparams2(params:any){
return function(target:any,paramsName:any,paamsIndex:any){
console.log('方法參數裝飾器2');
}
}
@logClass1("www.baidu.com")
@logClass2("www.IMOOC.com")
class HttpClient{
@logProperty()//可以不傳參
public apiUrl:string | undefined;
constructor(){
}
@logmethod('www.baidu.com')
getData(){
console.log(this.apiUrl);
}
setData(@logparams1('xxxxx'uuid:any),@logparams2('xxxxx'uuid:any)){
console.log(this.apiUrl);
}
}
var http:any = new HttpClient();
裝飾器執行順序
屬性》方法》方法參數》類
如果有多個同樣的裝飾器,它會先執行後面的裝飾器。