ES 6装飾器のDecoratorの基本的な使い方

2885 ワード

1.基本形式
@decorator
class A {}

//    

class A {}
A = decorator(A);
装飾器はjavascriptの中でクラスと属性だけを修飾して、関数を修飾することができません.装飾器のクラスに対する挙動の変化は、実行時ではなくコンパイル時に発生するものです.装飾器はコンパイル段階でコードを実行できます.装飾器は古典的なAOPモードの実現方式である.
2.飾り器の実行順序
同じところにある複数の飾り器は玉葱の模型に従って外から中に入り、内から外へ実行します.
function dec(id){
    console.log('evaluated', id);
    return (target, property, descriptor) => console.log('executed', id);
}

class Example {
    @dec(1)
    @dec(2)
    method(){}
}
// evaluated 1
// evaluated 2
// executed 2
// executed 1
 
3.一般的な装飾器の例
1.クラスはテストできます.属性を追加します.
 
@testable
class MyTestableClass {
  // ...
}

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

MyTestableClass.isTestable // true
 
より多くの構成を行うには、高次関数を使用して、パラメータを増加させることができ、例えば、特定のタイプの装飾器を作るための工場法に相当する.
//testable   Factory
function testable(isTestable) {
  return function(target) {
    target.isTestable = isTestable;
  }
}

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

@testable(false)
class MyClass {}
MyClass.isTestable // false
 
2.属性readonly装飾器
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;
}
 
3.ログデコラレータ
class Math {
  @log
  add(a, b) {
    return a + b;
  }
}

function log(target, name, descriptor) {
  var oldValue = descriptor.value;

  descriptor.value = function() {
    console.log(`Calling "${name}" with`, arguments);
    return oldValue.apply(null, arguments);
  };

  return descriptor;
}

const math = new Math();

// passed parameters should get logged now
math.add(2, 4);
 
3.memoize、予備録画モードを実現する
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;
}