ES6裝飾器Decorator基本用法

看風景就發表於2018-09-17

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;
}   

 

 

參考:https://www.cnblogs.com/goloving/p/8001530.html
     https://www.cnblogs.com/whitewolf/p/details-of-ES7-JavaScript-Decorators.html

相關文章