JS 装饰器

549 阅读2分钟

装饰器

装饰器的本质是一个函数,只能作用类、类静态、动态成员。语义结构清晰,能够在不破坏类内聚的基础上,给类提供额外功能特性。装饰器在编译期会被转译为相应的表达式语句。

作用于类

@decorator
class Person {}

// 相当于
class Person {};
Person = decorator(Person) || Person;


作用于属性/方法

class Person {
	@readonly
	name() {}
}
// 相当于,装饰器的放回值仅充当 `descriptor` 的作用,忽略返回非 `descriptor`的属性
Object.defineProperty(Person.prototype, 'name',  readonly(Person.prototype, 'name', Object.getOwnPropertyDescriptor(Person, 'name')) || Object.getOwnPropertyDescriptor(Person, 'name'));

装饰器可以接受参数


@decorator(true)
class Person {}

// 相当于
class Person {};
Person = decorator(true)(Person) || Person;

多个装饰器

// 作用于类
@dec(12)
@decorator(true)
class Person {}

// 相当于
class Person {};

// 执行顺序由外到内
const decFn = dec(12);
const decoratorFn = decorator(true)

// 执行顺序由内到外
Person = decoratorFn(Person) || Person;
Person = decFn(Person) || Person;
// 作用于属性/方法
class Person {
	@readonly()
	@override()
	name() {}
}

// 相当于
class Person {};

// 执行顺序由外到内
const read = readonly();
const over = override()

// 执行顺序由内到外
let descriptor = Object.getOwnPropertyDescriptor(Person, 'name');
Object.defineProperty(Person.prototype, 'name',  over(Person.prototype, 'name', descriptor) || descriptor);
descriptor = Object.getOwnPropertyDescriptor(Person, 'name');
Object.defineProperty(Person.prototype, 'name',  read(Person.prototype, 'name', descriptor) || descriptor);

使用前准备

由于装饰器在目前还是一个提案,因此使用使用装饰器语法的时候 ,需要设置babeltypescript的相关配置。

  • babel 需要安装语法支持插件 [babel-plugin-syntax-decorators](https://www.npmjs.com/package/babel-plugin-syntax-decorators)

  • typescript 需要配置tsconfig.json

    {
        "compilerOptions": {
            "target": "ES5",
            "experimentalDecorators": true, // 配置使用装饰器语法
        }
    }
    

第三方库

core-decorators.js,提供了几个常见的装饰器,部分如下:

- **@autobind**: `this`绑定
-  **@readonly**:属性只读
- **@override**:覆盖原方法
- **@deprecate** (**@deprecated**):废弃标识

属性描述对象 说明

每个属性都有一个对应的属性描述对象 ,根据属性的不同可以分为数据属性或存取属性,相应的属性描述对象的结构也有所差别。通过Object.defineProperty 修改属性描述对象,能够实现数据属性于存取属性的相互转换。

configurable(都有)enumerable(都有)valueediatblegetset
是否能修改属性描述对象false时赋值表达式仍能修改value属性是否能被枚举属性的值控制值能否被赋值表达式修改属性读取时调用属性赋值时调用
数据属性
存取属性