JavaScript中的装饰器--Decorator

什么是Decorator

  修饰模式(Decortaor),是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。就功能而言,修饰模式相比生成子类更为灵活,这样能够给某个对象而不是整个类添加一些功能。html

Decorator的做用

  经过使用修饰模式,能够在运行时扩充一个类的功能。原理是:增长一个修饰类包裹原来的类,包裹的方式通常是经过在将原来的对象做为修饰类的构造函数的参数。装饰类实现新的功能,可是,在不须要用到新功能的地方,它能够直接调用原来的类中的方法。修饰类必须和原来的类有相同的接口。
  修饰模式是类继承的另一种选择。类继承在编译时候增长行为,而装饰模式是在运行时增长行为。
  当有几个相互独立的功能须要扩充时,这个区别就变得很重要。在有些>面向对象的编程语言中,类不能在运行时被建立,一般在设计的时候也不能预测到有哪几种功能组合。这就意味著要为每一种组合建立一个新类。相反,修饰模式是面向运行时候的对象实例的,这样就能够在运行时根据须要进行组合。一个修饰模式的示例是JAVA里的Java I/O Streams的实现。java

  上面两段是维基百科中对于Decorator装饰器模式的介绍.简单来讲.Decorator就是一种动态地往一个类中添加新的行为的设计模式,它能够在类运行时,扩展一个类的功能.而且去修改类自己的属性和方法.使其能够在不一样类之间更灵活的共用一些属性和方法.下面就让咱们来看下在ES中Decorator的用法.node

Decorator的用法

类自己的修饰

  在ES中Decorator的具体表现形式为:es6

一个求值结果为函数的表达式,接受目标对象、名称和装饰器描述做为参数,可选地返回一个装饰器描述来安装到目标对象上。编程

因此说在ES中Decorator的最终本质就是一个函数,这个函数经过接受目标对象的三个参数: 所装饰的类的自己所装饰的类的某个属性的key值所装饰的类的某个属性的描述对象.并经过对这三个参数的操做,已达到为类扩展功能的目的.下面然咱们来用具体代码来演示一下:设计模式

@eat
class Person {
  constructor() {}
}

function eat(target, key, descriptor) {
  console.log('吃饭');
  console.log(target);
  console.log(key);
  console.log(descriptor);
  target.prototype.act = '我要吃饭';
}

const jack = new Person();
console.log(jack.act);

// 吃饭
// [Function: Person]
// undefined
// undefined
// 我要吃饭
复制代码

  上面是一个最简单的装饰器的运用.咱们首先声明一个类Person,而后在声明一个装饰器函数eat,在eat中将传入的三个参数分别打印出来,并将第一个参数target的原型prototype上添加一个属性act,并赋值为'我要吃饭'.而后将函数eat做为装饰在Person这个类自己上.最后,构造一个Person的实例jack,并打印jack上的act属性.浏览器

  而后从下面的运行结果中咱们能够看出,代码中会先打印出'吃饭',而后是参数target,其次是参数key,再而后是参数descriptor.最后才是jackact属性.这是由于装饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。这意味着,装饰器能在编译阶段运行代码。也就是说,装饰器本质就是编译时执行的函数。babel

类的属性的修饰

  看了上面那段代码的运行结果,你可能会用这么一个疑问.Decorator所传进来的三个参数: targetkeydescriptor.为何只有target有值,而keydescriptor则都是undefined了.事实上这是由于你将装饰器Decorator装饰在类自己上所致使的.在ES中装饰器并不单单只能装饰在类自己上,也能够装饰在类的属性上.当装饰在类的属性上时.keydescriptor也就有了用武之地.请看下面这段代码:编程语言

class Person {
    constructor() {}

    @test
    name() {console.log('张三');}
}

function test(target, key, descriptor) {
  console.log(target);
  console.log(key);
  console.log(descriptor);
}

const student = new Person();

student.name();

// Person {}
// name
// { value: [Function: name], writable: true, enumerable: false, configurable: true }
// 张三
复制代码

  在上面代码中咱们将test装饰器装饰在Person类的name属性上.而后打印三个传入的入参.分别获得了咱们指望的结果.而经过这三个参数,咱们就能够对咱们要装饰的对象进行一些有趣的修改, 以下面这样:函数

class Person {

  constructor() {}
    @test
    name() {console.log('张三');}
}

function test(target, key, descriptor) {
  descriptor.value = function () {
    console.log('李四');
  }
}

const student = new Person();

student.name();

// 李四
复制代码

  在上面代码中,咱们给一个类Person的原型上赋值了一个属性name,其值为一个函数,执行时会打印张三两个字.而后咱们给属性name装饰了test这个装饰器.在test装饰器中,将传入进来的descriptor对象上的value赋值为一个函数,执行时打印李四两个字.最后构造一个实例student,并执行name方法,执行的结果是打印了李四两个字,这说明经过装饰器,咱们彻底能够在不改变一个类自己的请况下对一个类的属性进行改写.这使得在不一样类中共享同一方法这一操做至关简单,且优雅.须要共享的使用使用装饰器,不须要的时候移除装饰器.彻底不用对类的自己进行操做.

  对于装饰器,若是咱们感到,固定传入的三个参数不够用的话,咱们也能够自行传入参数只须要像下面这么写:

function rename(name) {
  return function(target, key, descriptor) {
    descriptor.value = name;
  }
}

复制代码

前面已经说过,在ES中**Decorator就是一个求值结果为函数的表达式,**因此,只要你最后的返回结果是一个函数.都是一个合法的装饰器.

Decorator的兼容性

  目前ES中Decorator还处于提案阶段,各大浏览器和node,均未公开支持这一特性.若是想要使用,则须要借用babel的一个插件babel-plugin-transform-decorators才能够.

结束

  上面是我对Decorator的一些看法,但愿对你们有所帮助.若是文中任有何不当之处请予以斧正,谢谢

参考资料:

个人我的网址: wangyiming.info

相关文章
相关标签/搜索