wangbinyq / personal-wiki

2 stars 0 forks source link

javascript decorators #16

Open wangbinyq opened 7 years ago

wangbinyq commented 7 years ago

https://github.com/wycats/javascript-decorators

wangbinyq commented 7 years ago
wangbinyq commented 7 years ago

decorate class property

class Person {
  name() { return `${this.first} ${this.last}` }
}

Object.defineProperty(Person.prototype, 'name', {
  value: specifiedFunction,
  enumerable: false,
  configurable: true,
  writable: true
});

with decorator

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

let description = {
  type: 'method',
  initializer: () => specifiedFunction,
  enumerable: false,
  configurable: true,
  writable: true
};

description = readonly(Person.prototype, 'name', description) || description;
defineDecoratedProperty(Person.prototype, 'name', description);

function defineDecoratedProperty(target, { initializer, enumerable, configurable, writable }) {
  Object.defineProperty(target, { value: initializer(), enumerable, configurable, writable });
}

a simple decorator that memoizes an accessor

class Person {
  @memoize
  get name() { return `${this.first} ${this.last}` }
  set name(val) {
    let [first, last] = val.split(' ');
    this.first = first;
    this.last = last;
  }
}

let memoized = new WeakMap();
function memoize(target, name, descriptor) {
  let getter = descriptor.get, setter = descriptor.set;

  descriptor.get = function() {
    let table = memoizationFor(this);
    if (name in table) { return table[name]; }
    return table[name] = getter.call(this);
  }

  descriptor.set = function(val) {
    let table = memoizationFor(this);
    setter.call(this, val);
    table[name] = val;
  }
}

function memoizationFor(obj) {
  let table = memoized.get(obj);
  if (!table) { table = Object.create(null); memoized.set(obj, table); }
  return table;
}
wangbinyq commented 7 years ago

decorate class

The decorator takes the target constructor.

@annotation
class MyClass { }

function annotation(target) {
   // Add a property on target
   target.annotated = true;
}
wangbinyq commented 7 years ago

decorate factory

Since decorators are expressions, decorators can take additional arguments and act like a factory.

@isTestable(true)
class MyClass { }

function isTestable(value) {
   return function decorator(target) {
      target.isTestable = value;
   }
}
class C {
  @enumerable(false)
  method() { }
}

function enumerable(value) {
  return function (target, key, descriptor) {
     descriptor.enumerable = value;
     return descriptor;
  }
}
wangbinyq commented 7 years ago

Because descriptor decorators operate on targets, they also naturally work on static methods. The only difference is that the first argument to the decorator will be the class itself (the constructor) rather than the prototype, because that is the target of the original Object.defineProperty.

For the same reason, descriptor decorators work on object literals, and pass the object being created to the decorator.