JS的装饰器

2019-04-08  本文已影响0人  王小滚

什么是装饰器

在面向对象(OOP)的设计模式中,decorator被称为装饰模式。我们在程序开发中,许多时候并不希望某个类天生就非常庞大,一次性包含许多职责。装饰者模式可以动态地给某个对象添加一些额外的功能,而不会影响从这个类中派生的其他对象。

JS的装饰器

为了更简便的使用装饰器模式,ES7引入了一个新的语法,依赖于 ES5 的Object.defineProperty方法。

修饰器的用法

类的装饰
class A {}

// 等同于

class A {}
A = decorator(A) || A;

也就是说,装饰器是一个对类进行处理的函数。装饰器函数的第一个参数,就是所要修饰的目标类。
如果觉得一个参数不够用,可以在装饰器外面再封装一层函数。

function testable(isTestable) {
  return function(target) {
    target.isTestable = isTestable;
  }
}

@testable(true)
class MyTestableClass {}
MyTestableClass.isTestable // true

@testable(false)
class MyClass {}
MyClass.isTestable // false

简单来说JS 的装饰器可以用来“装饰”三种类型的对象:类的属性/方法、访问器、类本身

针对属性/方法的装饰器
class Person {
    @readonly
    name() { return `${this.first} ${this.last}` }
}
function readonly(target, name, descriptor){
  // descriptor对象原来的值如下
  // {
  //   value: specifiedFunction,
  //   enumerable: false,
  //   configurable: true,
  //   writable: true
  // };
  descriptor.writable = false;
  return descriptor;
}

readonly(Person.prototype, 'name', descriptor);
// 类似于
Object.defineProperty(Person.prototype, 'name', descriptor);
函数方法的装饰

由于存在函数提升,使得修饰器不能用于函数。类是不会提升的,所以就没有这方面的问题。
可以采用高阶函数的形式直接执行。

function doSomething(name) {
  console.log('Hello, ' + name);
}

function loggingDecorator(wrapped) {
  return function() {
    console.log('Starting');
    const result = wrapped.apply(this, arguments);
    console.log('Finished');
    return result;
  }
}

const wrapped = loggingDecorator(doSomething);
// decorator 外部可以包装一个函数,函数可以带参数
function Decorator(type){
    /**
     * 这里是真正的 decorator
     * @target 装饰的属性所述的类的原型,注意,不是实例后的类。如果装饰的是 Car 的某个属性,这个 target 的值就是 Car.prototype
     * @name 装饰的属性的 key
     * @descriptor 装饰的对象的描述对象
     */
    return function (target, name, descriptor){
        // 以此可以获取实例化的时候此属性的默认值
        let v = descriptor.initializer && descriptor.initializer.call(this);
        // 返回一个新的描述对象,或者直接修改 descriptor 也可以
        return {
            enumerable: true,
            configurable: true,
            get: function() {
                return v;
            },
            set: function(c) {
                v = c;
            }
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读