- 装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为。
- 通俗的讲,装饰器就是一个方法,可以注入到类,方法,属性或参数上来扩展类,方法,属性或参数的功能。
- 常见的装饰器有:类装饰器,属性装饰器,方法装饰器,参数装饰器。
- 装饰器的写法:普通装饰器(无法传参),装饰器工厂(可传参)
- 装饰器是过去几年中
JS最大的成就之一,已是 ES7 的标准特性之一。
类装饰器
类装饰器在类声明之前被声明(紧靠着类声明)。
- 传入一个参数。类装饰器应用于类构造函数,可以用来监视修改或替换类定义。
一 普通装饰器(无法传参)
// 装饰器
function logClass(params: any) {
// params 就是当前类,可以使用params扩展当前类
console.log(params); // ƒ HttpClient() { }
// 动态扩展属性
params.prototype.url = '普通装饰器-----在不改变原来类的前提下,动态扩展属性';
// 动态扩展方法
params.prototype.run = function() {
console.log('普通装饰器-----在不改变原来类的前提下,动态扩展方法');
}
}
// --普通装饰器(不可传参)
// 不带参数时,获得的直接是 该类
@logClass
class HttpClient {
constructor() {
}
getData(): void {
}
}
let http: any = new HttpClient();
console.log(http.url); // 普通装饰器-----在不改变原来类的前提下,动态扩展属性
http.run(); // 普通装饰器-----在不改变原来类的前提下,动态扩展方法
二 装饰器工厂(可传参)
// 装饰器工厂----可传参
function logClass(params: string) {
return function(target: any) {
console.log('params----', params); // params---- http://www.baidu.com/api
console.log('target----', target); // target---- ƒ HttpClient() { }
// 动态扩展属性
target.prototype.url = params;
// 动态扩展方法
target.prototype.run = function() {
console.log('工厂装饰器-----在不改变原来类的前提下,动态扩展方法');
}
}
}
// 不带参数时,获得的直接是 参数+该类
@logClass('http://www.baidu.com/api')
class HttpClient {
constructor() {
}
}
let http: any = new HttpClient();
console.log(http.url); // http://www.baidu.com/api
http.run(); // 工厂装饰器-----在不改变原来类的前提下,动态扩展方法
三 类装饰器重载构造函数
- 类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。
- 如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明。
- 执行顺序:装饰器里的内容--->类的构造函数--->类中的方法(针对下方实例)。
// 类装饰器重载构造函数
function logClass(target: any) {
console.log(target);
// ƒ HttpClient() {
// this.apiUrl = 'apiUrl';
// console.log(11);
// }
return class extends target {
apiUrl: any = '我是类装饰器的数据';
getData(): void {
console.log(`这是类装饰器里面的apiUrl----${this.apiUrl}`); // 这是类装饰器里面的apiUrl----我是类装饰器的数据
}
}
}
// 不带参数时,获得的直接是 该类
@logClass
class HttpClient {
public apiUrl: string | undefined;
constructor() {
this.apiUrl = 'apiUrl';
console.log(11); // 11
}
getData(): void {
console.log(`这是构造函数里面的apiUrl-${this.apiUrl}`);
}
}
let http = new HttpClient();
http.getData(); // 这是构造函数里面的apiUrlapiUrl
输出
ƒ HttpClient() {
this.apiUrl = 'apiUrl';
console.log(11);
}
11
这是类装饰器里面的apiUrl----我是类装饰器的数据
属性装饰器
- 属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数
- 1 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- 2 成员的名字
// 属性装饰器
function logProperty(params: any) {
console.log('属性装饰器--params-', params); // 属性装饰器--params- http://www.itying.com/api
return function(target: any, attr: any) {
console.log('属性装饰器-target', target);
// 属性装饰器-target {getData: ƒ, getData_1: ƒ, constructor: ƒ}
// getData: ƒ ()
// getData_1: ƒ ()
// url: "http://www.itying.com/api"
// constructor: ƒ HttpClient()
// __proto__: Object
// ... ...
console.log('属性装饰器-attr', attr); // 属性装饰器-attr url
target[attr] = params; // 将属性装饰器传入的参数赋值给
}
}
class HttpClient {
@logProperty('http://www.itying.com/api')
public url: any | undefined;
public url_1: any | undefined;
constructor() {
this.url_1 = '这是url_1';
}
getData(): void {
console.log('this.url--', this.url); // this.url-- http://www.itying.com/api
}
getData_1(): void {
console.log('this.url_1--', this.url_1);
}
}
let http: any = new HttpClient();
http.getData();
输出
属性装饰器--params- http://www.itying.com/api
属性装饰器-target {getData: ƒ, getData_1: ƒ, constructor: ƒ}getData: ƒ ()getData_1: ƒ ()url: "http://www.itying.com/api"constructor: ƒ HttpClient()__proto__: Objectconstructor: ƒ Object()hasOwnProperty: ƒ hasOwnProperty()isPrototypeOf: ƒ isPrototypeOf()propertyIsEnumerable: ƒ propertyIsEnumerable()toLocaleString: ƒ toLocaleString()toString: ƒ toString()valueOf: ƒ valueOf()__defineGetter__: ƒ __defineGetter__()__defineSetter__: ƒ __defineSetter__()__lookupGetter__: ƒ __lookupGetter__()__lookupSetter__: ƒ __lookupSetter__()get __proto__: ƒ __proto__()set __proto__: ƒ __proto__()
属性装饰器-attr url
this.url-- http://www.itying.com/api
方法装饰器
它会被应用到方法的属性描述符上,可以用来监视、修改或者替换方法定义。方法装饰器会在运行时传入下列3个参数:
- 1 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- 2 成员的名字。
- 3 成员的属性描述符。
一 可以扩展属性、方法
// 方法装饰器
function GET(params: any) {
console.log('方法装饰器--params-', params); // 方法装饰器--params- http://www.baidu.com
return function(target: any, methodName: any, desc: any) {
console.log('方法装饰器-target', target);
// 方法装饰器-target
// {getData: ƒ, getData_1: ƒ, constructor: ƒ}
// getData: ƒ ()
// getData_1: ƒ ()
// name: "xxx"
// run: ƒ ()
// constructor: ƒ HttpClient()
// __proto__: Object
console.log('方法装饰器-methodName', methodName); // 方法装饰器-methodName getData
console.log('方法装饰器-desc', desc);
// 方法装饰器-desc
// {writable: true, enumerable: true, configurable: true, value: ƒ}
// configurable: true
// enumerable: true
// value: ƒ ()
// writable: true
// __proto__: Object
// 扩展
target.name = 'xxx';
target.run = function() {
console.log('run');
}
}
}
class HttpClient {
public url: any | undefined;
public url_1: any | undefined;
constructor() {
this.url = '这是构造函数的URL';
}
@GET('http://www.baidu.com')
getData(): void {
console.log('this.url--', this.url); // this.url-- 这是构造函数的URL
}
getData_1(): void {
}
}
let http: any = new HttpClient();
http.getData();
http.run(); // run
console.log(http.name); // xxx
输出
方法装饰器--params- http://www.baidu.com
方法装饰器-target {getData: ƒ, getData_1: ƒ, constructor: ƒ}getData: ƒ ()getData_1: ƒ ()name: "xxx"run: ƒ ()constructor: ƒ HttpClient()__proto__: Object
方法装饰器-methodName getData
方法装饰器-desc {writable: true, enumerable: true, configurable: true, value: ƒ}configurable: trueenumerable: truevalue: ƒ ()writable: true__proto__: Object
this.url-- 这是构造函数的URL
run
xxx
二 可修改方法内容
// 方法装饰器 -- 可修改方法内容
function GET(params: any) {
console.log('方法装饰器--params-', params); // 方法装饰器--params- http://www.baidu.com
return function(target: any, methodName: any, desc: any) {
console.log('方法装饰器-target', target);
// 方法装饰器-target
// {getData: ƒ, getData_1: ƒ, constructor: ƒ}
// getData: ƒ ()
// getData_1: ƒ ()
// name: "xxx"
// run: ƒ ()
// constructor: ƒ HttpClient()
// __proto__: Object
console.log('方法装饰器-methodName', methodName); // 方法装饰器-methodName getData
console.log('方法装饰器-desc', desc);
// 方法装饰器-desc
// {writable: true, enumerable: true, configurable: true, value: ƒ}
// configurable: true
// enumerable: true
// value: ƒ ()
// writable: true
// __proto__: Object
// 修改装饰器的方法 把装饰器方法里面传入的所有参数改为 string类型
// 1 保存当前的方法
let oMethod = desc.value;
desc.value = function(...args: any[]) {
args = args.map(value => {
return String(value);
})
console.log('这是通过方法装饰器修改后的 方法'); // 这是通过方法装饰器修改后的 方法
console.log(args); // (3) ["123", "23", "true"]
oMethod.apply(this, args); // this 指得是当前的function
}
}
}
class HttpClient {
public url: any | undefined;
public url_1: any | undefined;
constructor() {
this.url = '这是构造函数的URL';
}
@GET('http://www.baidu.com')
getData(): void {
console.log('this.url--', this.url); // this.url-- 这是构造函数的URL
}
@GET('http://www.baidu.com/api')
getData_1(...args: any[]): void {
console.log('args--', args); // (3) ["34", "45", "false"]
}
}
let http: any = new HttpClient();
http.getData(123,'23',true);
http.getData_1(34,'45',false);
输出
方法装饰器--params- http://www.baidu.com
方法装饰器-target {getData: ƒ, getData_1: ƒ, constructor: ƒ}
方法装饰器-methodName getData
方法装饰器-desc {writable: true, enumerable: true, configurable: true, value: ƒ}
方法装饰器--params- http://www.baidu.com/api
方法装饰器-target {getData: ƒ, getData_1: ƒ, constructor: ƒ}
方法装饰器-methodName getData_1
方法装饰器-desc {writable: true, enumerable: true, configurable: true, value: ƒ}
这是通过方法装饰器修改后的 方法
(3) ["123", "23", "true"]
this.url-- 这是构造函数的URL
这是通过方法装饰器修改后的 方法
(3) ["34", "45", "false"]
args-- (3) ["34", "45", "false"]
方法参数装饰器
参数装饰器表达式会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元素数据,传入下列3个参数
- 1 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
- 2 参数的名字。
- 3 参数在函数参数列表中的索引
先调用 参数装饰器
// ------- 方法参数装饰器
function methodParams(params: any) {
console.log('方法参数装饰器---params--', params);
return function(target: any, methodName: any, paramsIndex: any) {
console.log('方法参数装饰器---target--', target);
console.log('方法参数装饰器---methodName--', methodName);
console.log('方法参数装饰器---paramsIndex--', paramsIndex);
}
}
class HttpClient {
public url: any | undefined;
constructor() {
this.url = '这是构造函数的URL';
}
getData(@methodParams('uuid') uuid: any, @methodParams('apptoken') apptoken: any, @methodParams('hh') hh: any): void {
console.log('this.url--', this.url); // this.url-- 这是构造函数的URL
console.log(`uuid--${uuid},apptoken--${apptoken},hh--${hh}`); // this.url-- 这是构造函数的URL
}
}
let http: any = new HttpClient();
http.getData(123,'23',true);
输出
方法参数装饰器---params-- uuid
方法参数装饰器---params-- apptoken
方法参数装饰器---params-- hh
方法参数装饰器---target-- {getData: ƒ, constructor: ƒ}getData: ƒ (uuid, apptoken, hh)constructor: ƒ HttpClient()__proto__: Object
方法参数装饰器---methodName-- getData
方法参数装饰器---paramsIndex-- 2
方法参数装饰器---target-- {getData: ƒ, constructor: ƒ}getData: ƒ (uuid, apptoken, hh)constructor: ƒ HttpClient()__proto__: Object
方法参数装饰器---methodName-- getData
方法参数装饰器---paramsIndex-- 1
方法参数装饰器---target-- {getData: ƒ, constructor: ƒ}getData: ƒ (uuid, apptoken, hh)constructor: ƒ HttpClient()__proto__: Object
方法参数装饰器---methodName-- getData
方法参数装饰器---paramsIndex-- 0
this.url-- 这是构造函数的URL
uuid--123,apptoken--23,hh--true
装饰器执行顺序
属性装饰器-->方法装饰器-->方法参数装饰器-->类装饰器。
若 同样的装饰器有多个,顺序:从右到左,或从下到上
// 类装饰器
function logClass(params: any) {
return function(target: any) {
console.log('类装饰器');
}
}
// 类装饰器1
function logClass1(params: any) {
return function(target: any) {
console.log('类装饰器1');
}
}
// 属性装饰器
function Url(params: any) {
return function(target: any, methodName: any) {
console.log('属性装饰器');
}
}
// 方法装饰器
function GET(params: any) {
return function(target: any, methodName: any, desc: any) {
console.log('方法装饰器');
}
}
// 参数装饰器
function methodParams(params: any) {
return function(target: any, methodName: any, paramsIndex: any) {
console.log('参数装饰器');
}
}
@logClass1('HttpClient1')
@logClass('HttpClient')
class HttpClient {
@Url('http://www.baidu.com/api')
public url: any | undefined;
constructor() {
this.url = '这是构造函数的URL';
}
@GET('/api/list')
getData(@methodParams('username') username: string, @methodParams('password') password: string): void {
console.log(`URL:${this.url}--username:${username}--password:${password}`);
}
}
let http: any = new HttpClient();
http.getData('admin','123456');