问答题1417/1593你是怎么理解ES6中 Decorator 的?使用场景有哪些?

难度:
2021-07-03 创建

参考答案:

一、介绍

Decorator,即装饰器,从名字上很容易让我们联想到装饰者模式

简单来讲,装饰者模式就是一种在不改变原类和使用继承的情况下,动态地扩展对象功能的设计理论。

ES6Decorator功能亦如此,其本质也不是什么高大上的结构,就是一个普通的函数,用于扩展类属性和类方法

这里定义一个士兵,这时候他什么装备都没有

1class soldier{ 2}

定义一个得到 AK 装备的函数,即装饰器

1function strong(target){ 2 target.AK = true 3}

使用该装饰器对士兵进行增强

1@strong 2class soldier{ 3}

这时候士兵就有武器了

1soldier.AK // true

上述代码虽然简单,但也能够清晰看到了使用Decorator两大优点:

  • 代码可读性变强了,装饰器命名相当于一个注释
  • 在不改变原有代码情况下,对原来功能进行扩展

二、用法

Docorator修饰对象为下面两种:

  • 类的装饰
  • 类属性的装饰

类的装饰

当对类本身进行装饰的时候,能够接受一个参数,即类本身

将装饰器行为进行分解,大家能够有个更深入的了解

1@decorator 2class A {} 3 4// 等同于 5 6class A {} 7A = decorator(A) || A;

下面@testable就是一个装饰器,target就是传入的类,即MyTestableClass,实现了为类添加静态属性

1@testable 2class MyTestableClass { 3 // ... 4} 5 6function testable(target) { 7 target.isTestable = true; 8} 9 10MyTestableClass.isTestable // true

如果想要传递参数,可以在装饰器外层再封装一层函数

1function testable(isTestable) { 2 return function(target) { 3 target.isTestable = isTestable; 4 } 5} 6 7@testable(true) 8class MyTestableClass {} 9MyTestableClass.isTestable // true 10 11@testable(false) 12class MyClass {} 13MyClass.isTestable // false

类属性的装饰

当对类属性进行装饰的时候,能够接受三个参数:

  • 类的原型对象
  • 需要装饰的属性名
  • 装饰属性名的描述对象

首先定义一个readonly装饰器

1function readonly(target, name, descriptor){ 2 descriptor.writable = false; // 将可写属性设为false 3 return descriptor; 4}

使用readonly装饰类的name方法

1class Person { 2 @readonly 3 name() { return `${this.first} ${this.last}` } 4}

相当于以下调用

1readonly(Person.prototype, 'name', descriptor);

如果一个方法有多个装饰器,就像洋葱一样,先从外到内进入,再由内到外执行

1function dec(id){ 2 console.log('evaluated', id); 3 return (target, property, descriptor) =>console.log('executed', id); 4} 5 6class Example { 7 @dec(1) 8 @dec(2) 9 method(){} 10} 11// evaluated 1 12// evaluated 2 13// executed 2 14// executed 1

外层装饰器@dec(1)先进入,但是内层装饰器@dec(2)先执行

注意

装饰器不能用于修饰函数,因为函数存在变量声明情况

1var counter = 0; 2 3var add = function () { 4 counter++; 5}; 6 7@add 8function foo() { 9}

编译阶段,变成下面

1var counter; 2var add; 3 4@add 5function foo() { 6} 7 8counter = 0; 9 10add = function () { 11 counter++; 12};

意图是执行后counter等于 1,但是实际上结果是counter等于 0

三、使用场景

基于Decorator强大的作用,我们能够完成各种场景的需求,下面简单列举几种:

使用react-redux的时候,如果写成下面这种形式,既不雅观也很麻烦

1class MyReactComponent extends React.Component {} 2 3export default connect(mapStateToProps, mapDispatchToProps)(MyReactComponent);

通过装饰器就变得简洁多了

1@connect(mapStateToProps, mapDispatchToProps) 2export default class MyReactComponent extends React.Component {}

mixins,也可以写成装饰器,让使用更为简洁了

1function mixins(...list) { 2 return function (target) { 3 Object.assign(target.prototype, ...list); 4 }; 5} 6 7// 使用 8const Foo = { 9 foo() { console.log('foo') } 10}; 11 12@mixins(Foo) 13class MyClass {} 14 15let obj = new MyClass(); 16obj.foo() // "foo"

下面再讲讲core-decorators.js几个常见的装饰器

@antobind

autobind装饰器使得方法中的this对象,绑定原始对象

1import { autobind } from 'core-decorators'; 2 3class Person { 4 @autobind 5 getPerson() { 6 return this; 7 } 8} 9 10let person = new Person(); 11let getPerson = person.getPerson; 12 13getPerson() === person; 14// true

@readonly

readonly装饰器使得属性或方法不可写

1import { readonly } from 'core-decorators'; 2 3class Meal { 4 @readonly 5 entree = 'steak'; 6} 7 8var dinner = new Meal(); 9dinner.entree = 'salmon'; 10// Cannot assign to read only property 'entree' of [object Object]

@deprecate

deprecatedeprecated装饰器在控制台显示一条警告,表示该方法将废除

1import { deprecate } from 'core-decorators'; 2 3class Person { 4 @deprecate 5 facepalm() {} 6 7 @deprecate('功能废除了') 8 facepalmHard() {} 9} 10 11let person = new Person(); 12 13person.facepalm(); 14// DEPRECATION Person#facepalm: This function will be removed in future versions. 15 16person.facepalmHard(); 17// DEPRECATION Person#facepalmHard: 功能废除了 18

最近更新时间:2024-07-20

赞赏支持

预览

题库维护不易,您的支持就是我们最大的动力!