2017-05-13 3 views
2

속성 (1)을 오버라이드하고 숨겨진 속성 (2)을 정의하는 데코레이터를 구현하려고합니다. 다음 예제를 가정합니다Typescript 데코레이터와 Object.defineProperty 이상한 동작

function f() { 
    return (target: any, key: string) => { 

     let pKey = '_' + key; 

     // 1. Define hidden property 
     Object.defineProperty(target, pKey, { 
      value: 0, 
      enumerable: false, 
      configurable: true, 
      writable: true 
     }); 

     // 2. Override property get/set 
     return Object.defineProperty(target, key, { 
      enumerable: true, 
      configurable: true, 
      get:() => target[pKey], 
      set: (val) => { 
       target[pKey] = target[pKey] + 1; 
      } 
     }); 
    }; 
} 

class A { 
    @f() 
    propA = null; 
    propB = null; 
} 

let a = new A(); 

console.log(Object.keys(a), a.propA, a._propA, a); 

출력한다 :

[ 'propB' ] 1 1 A { propB: null } 

그러나, 나는 오히려 기대 : enumerable 이후

[ 'propA', 'propB' ] 1 1 A { propA: 1, propB: null } 

propA에 대한 true입니다. 내가

get: function() { 
    return this[pKey] 
}, 
set: function (val) { 
    this[pKey] = this[pKey] + 1; 
} 

getset를 교체하는 경우

이제, 출력은 지금 : enumerable 비록

[ '_propA', 'propB' ] 1 1 A { _propA: 1, propB: null } 

이 명시 적으로 f에서 _propA에 대한 false로 설정됩니다.

이러한 동작이 이상 할 수 있으므로 여기에서 무슨 일이 일어나고 있는지, 내가 얻으려고하는 것을 어떻게 구현하는지 알고 싶습니다.

답변

2

좋아요, 그래서 시간이 좀 걸렸지 만 해결 방법을 찾았습니다. 문제는 장식시에 Object.defineProperty이 제대로 작동하지 않는 것 같습니다. 런타임에 실행하면 예상대로 작동합니다. 그렇다면 데코레이터 안에서 속성을 어떻게 정의 할 수 있습니까?

다음은 속임수입니다. 데코레이터 내부의 속성을 재정의하면 장식 시간에 작동하므로 (열거 가능한 동작 만 손상된 것 같습니다) gettersetter 대신 초기화 기능을 사용할 수 있습니다. 이 함수는 속성이 처음 할당되거나 (set) 액세스되거나 (get) 액세스 할 때 실행됩니다. 이 경우 단어 this이 개체의 런타임 인스턴스를 참조하므로 장식시에 의도 한 작업을 올바르게 초기화 할 수 있습니다.

function f() { 
    return (target: any, key: string) => { 
     let pKey = `_${key}`; 

     let init = function (isGet: boolean) { 
      return function (newVal?) { 
       /* 
       * This is called at runtime, so "this" is the instance. 
       */ 

       // Define hidden property 
       Object.defineProperty(this, pKey, {value: 0, enumerable: false, configurable: true, writable: true}); 
       // Define public property 
       Object.defineProperty(this, key, { 
        get:() => { 
         return this[pKey]; 
        }, 
        set: (val) => { 
         this[pKey] = this[pKey] + 1; 
        }, 
        enumerable: true, 
        configurable: true 
       }); 

       // Perform original action 
       if (isGet) { 
        return this[key]; // get 
       } else { 
        this[key] = newVal; // set 
       } 
      }; 
     }; 

     // Override property to let init occur on first get/set 
     return Object.defineProperty(target, key, { 
      get: init(true), 
      set: init(false), 
      enumerable: true, 
      configurable: true 
     }); 
    }; 
} 

출력 : 올바른 GET/세트가 초기화 된 후 그들이 할당되므로

[ 'propA', 'propB' ] 1 1 A { propA: [Getter/Setter], propB: null } 

이 솔루션은 기본 값을 지원 여기

는 솔루션입니다. 또한 제대로 enumerable을 지원

: 부동산 pKey에 대한 trueenumerable를 설정하고 출력은 다음과 같습니다

[ '_propA', 'propA', 'propB' ] 1 1 A { _propA: 1, propA: [Getter/Setter], propB: null } 

그것은 나는 것을하지 꽤 알고,하지만 그것은 작동하고 I로까지 성능이 저하되지 않습니다 알고있다.