修饰器Decorator笔记

简介

修饰器是一种特殊类型的声明,它只能够被附加到类的声明、方法、属性或参数上,可以修改类的行为。而不能用于函数(因为存在函数提升)

常见的修饰器有:类修饰器属性修饰器方法修饰器参数修饰器

修饰器写法:普通修饰器(无法传参)、修饰器工厂(可传参)

修饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。修饰器能在编译阶段运行代码。也就是说,修饰器本质就是编译时执行的函数

修饰器是一个对类进行处理的函数。修饰器函数的第一个参数,就是所要修饰的目标类

修饰器的行为类似如下

@decorator class A {} // 等同于 class A {} A = decorator(A) || A;

类修饰器

类修饰器在类声明之前被声明(紧靠着类声明)

类修饰器应用于类构造函数,可以用来监视,修改或替换类定义

  • 类修饰器例子
function logClass (target) { console.log(target) // [Function: MyTest] target指向修饰的类 target.nTest = '扩展的静态属性' // 扩展静态属性 target.prototype.nName = '动态扩展的属性' // 给原型扩展属性 target.prototype.nFun = () => { console.log('动态扩展的方法') } } @logClass class MyTest {} let test = new MyTest() console.log(test.nTest) // 扩展的静态属性 console.log(test.nName) // 动态扩展的属性 test.nFun() // 动态扩展的方法
  • 修饰器工厂(闭包传参)
function logClass (params) { return function (target) { console.log(target) // [Function: MyTest] console.log(params) // hello target.prototype.nName = '动态扩展的属性' target.prototype.nFun = () => { console.log('动态扩展的方法') } } } @logClass('hello') class MyTest {} let test = new MyTest() console.log(test.nName) // 动态扩展的属性 test.nFun() // 动态扩展的方法
  • Mixins混入例子
// mixins.js 可以返回一个函数 export function mixins(...list) { return function(target) { Object.assign( target.prototype, ...list ) } } // main.js import { mixins } from './mixins.js' const Foo = { foo() {console.log('foo')} } @mixins(Foo) // 当函数调用,传入参数 class MyClass {} let obj = new MyClass() obj.foo // 'foo'
  • 重载构造函数的例子
function logClass (target) { return class extends target { attr = '重载属性' getData () { console.log('重载方法', this.attr) } } } @logClass class MyTest { attr constructor () { this.attr = '构造函数的属性' } getData () { console.log(this.attr) } } let test = new MyTest() test.getData() // 重载方法 重载属性
  • React + Redux 例子
class MyReactComponent extends React.Component {} export default connect(mapStateToProps, mapDispatchToProps)(MyReactComponent); // 可改写成 @connect(mapStateToProps, mapDispatchToProps) export default class MyReactComponent extends React.Component {}

属性修饰器

属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数

  1. 对于静态成员来说时类的构造函数,对于实例成员是类的原型对象
  2. 修饰的属性名
function logProperty (params) { return function (target, name) { console.log(target, name) // MyTest { getData: [Function] } 'attr' target[name] = params } } class MyTest { @logProperty('属性修饰器的参数') attr constructor () {} getData () { console.log(this.attr) // 属性修饰器的参数 } } let test = new MyTest() test.getData()

方法修饰器

它会被应用到方法的属性描述符上,可以用来监听、修改或者替换方法定义
修饰器会修改属性的描述对象,然后被修改的描述对象再用来定义属性
方法修饰器会在运行时传入下列3个参数:

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
  2. 修饰的属性名
  3. 该属性的描述对象
  • 方法修饰器例子
// 方法修饰器 function logFunction (params) { return function (target, methodName, desc) { console.log(target, methodName, desc) /* MyTest { getData: [Function] } 'getData' { value: [Function], // 值 writable: true, // 可读 enumerable: true, // 可枚举 configurable: true // 可设置 } */ target.nName = '动态扩展的属性' target.nFun = () => { console.log('动态扩展的方法') } // 将接收到的参数改为 string 类型 let oMethod = desc.value desc.value = function (...args) { args = args.map((v) => { return String(v) }) return oMethod.apply(this, args) } } } class MyTest { @logFunction('方法修饰器的参数') getData (...args) { console.log(args) } } let test = new MyTest() console.log(test.nName) //动态扩展的属性 test.nFun() // 动态扩展的方法 test.getData(123, '234', () => {}) // [ '123', '234', 'function () { }' ]
  • 输出日志的例子
class Math { @log add(a, b) { return a + b; } } // @log修饰器的作用就是在执行原始的操作之前,执行一次console.log,从而达到输出日志的目的 function log(target, name, descriptor) { var oldValue = descriptor.value; descriptor.value = function() { console.log(`Calling ${name} with`, arguments); return oldValue.apply(this, arguments); }; return descriptor; } const math = new Math(); // passed parameters should get logged now math.add(2, 4);
  • 修饰器有注释的作用,看上去一目了然
@Component({ tag: 'my-component', styleUrl: 'my-component.scss' }) export class MyComponent { @Prop() first: string; // props @Prop() last: string; // props @State() isVisible: boolean = true; // props render() { return ( <p>Hello, my name is {this.first} {this.last}</p> ); } }

参数修饰器

参数修饰器表达式会在运行是当作函数被调用,可以使用参数修饰器为类的原型增加一些元素数据,传入下列3个参数

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
  2. 参数的名字
  3. 参数在函数参数列表中的索引
function logParams (params) { return function (target, methodName, paramsIndex) { console.log(target, methodName, paramsIndex) // MyTest { getData: [Function] } 'getData' 0 target.param = params } } class MyTest { getData (@logParams('参数修饰符的参数') id) { console.log(id) } } let test = new MyTest() test.getData(123) console.log(test.param) // 参数修饰符的参数

执行顺序

  • 属性修饰器 > 参数修饰器 > 方法修饰器 > 类修饰器
function log (params) { return function () { console.log(params) } } @log('类修饰器') class MyTest { @log('属性修饰器') id @log('方法修饰器') getData (@log('参数修饰器') id) { this.id = id } } new MyTest()
  • 同一个方法有多个修饰器,会像剥洋葱一样,先从外到内进入,然后由内向外执行
function dec(id){ console.log('evaluated', id); return (target, property, descriptor) => console.log('executed', id); } class Example { @dec(1) @dec(2) method(){} } // evaluated 1 // evaluated 2 // executed 2 // executed 1

不能用于函数

修饰器只能用于类和类的方法,不能用于函数,因为存在函数提升。

// 意图是执行后counter等于 1,但是实际上结果是counter等于 0 var counter = 0; var add = function () { counter++; }; @add function foo() { }

上面代码,函数提升后相当于

@add function foo() { } var counter; var add; counter = 0; add = function () { counter++; };

创作不易,若本文对你有帮助,欢迎打赏支持作者!

 分享给好友: