자바스크립트(ES2015~) 프록시에서 속성에 후크하는 올바른 방법

이 기사에 대하여



ES2015(ES6)에서 추가된 Proxy 객체를 사용하면 속성에 대한 액세스를 중단하고 원하는 작업을 수행할 수 있습니다.

단, 사용법에 조금 요령이 있습니다. 이 기사에서는 넷상에서 산견되는 「좋지 않은 예」에 대해, 무엇이 어떻게 좋지 않은지를 설명해, 주의해야 할 포인트를 소개합니다.

좋지 않은 예



이런 느낌의 코드가 소개되고 있는 경우가 있습니다만, 그다지 좋지 않습니다.
/*
常に値を2倍にするProxy
*/
function doubleProxy_Bad(target) {
    return new Proxy(target, {
        get(target, name) {
            //もとのプロパティの値を取得
            const orig = target[name];
            //2倍にして返す
            return orig * 2;
        },  
        set(target, name, value) {
            //値を2倍にする
            const modified = value * 2;
            //もとのプロパティに設定
            target[name] = modified;
        }   
    }); 
}

올바른 예



보다 정확하게는 이것입니다.
function doubleProxy_Good(target) {
    return new Proxy(target, {
        get(target, name, receiver) {
            //もとのプロパティの値を取得
            const orig = Reflect.get(target, name, receiver);
            //2倍にして返す
            return orig * 2;
        },  
        set(target, name, value, receiver) {
            //値を2倍にする
            const modified = value * 2;
            //もとのプロパティに設定
            Reflect.set(target, name, modified, receiver);
        }   
    }); 
}

차이점은 다음 두 가지입니다.
  • receiver 인수를 올바르게 처리 중(get/set의 마지막 인수)
  • Reflect 객체 사용

  • get/set 에는 receiver 라는 인수가 있다



    receiver의 정체는 Proxy 객체 그 자체입니다. 다음 코드로 확인할 수 있습니다.
    const o = { a: 10 };
    
    //どんなプロパティにアクセスしてもreceiverを返す
    const p = new Proxy(o, {
        get(target, name, receiver) {
            return receiver;
        }
    });
    
    console.log(p.a === p);
    

    언뜻 보면이 인수가 무엇을 위해 존재하는지 모르겠습니다. 그러나 getter/setter가 정의되면 큰 차이가 있습니다.

    다음과 같은 예를 생각해 봅시다.
    const obj = {
        a: 1,
        b: 5,
        get c() {
            return this.a + this.b;
        }
    };
    
    const p = doubleProxy(obj);
    
    console.log(p.c); //=> ???
    
    p.c 결과는 12입니까? 24일까요?

    즉, get cthis는 원래 개체입니까? 아니면 프록시입니까?

    처음에 소개한 "좋지 않은 예"라면, this 는 반드시 원래의 오브젝트가 되어, 결과는 12가 됩니다. 올바른 예에서 this는 프록시가 되고 24가 됩니다.
    console.log(doubleProxy_Bad(obj).c);  //=> 12
    console.log(doubleProxy_Good(obj).c); //=> 24
    

    어떤 동작이 바람직한지는 사례별 경우입니다. 그러나 Proxy가 아니라면 곤란하다는 경우가 있을 것입니다.

    예를 들어 getter/setter 안에서 메소드를 호출하는 경우에는, this 의 값에 의해, 그 메소드 호출에도 Proxy의 효과가 미치는지 어떤지가 정해집니다.

    Reflect 객체를 사용합시다.



    receiver 인수와 Reflect 객체는 그렇게합니다.
    Reflect.get(target, name[, receiver]);
    Reflect.set(target, name, value[, receiver]);
    
    Reflect.getReflect.set의 인수에 receiver를 주면, 그 값이 getter/setter 호출의 this가 됩니다.

    생략하면 target이 this입니다. 즉 원래의 객체를 직접 참조하는 것과 같습니다.

    어느 쪽이 자신이 원하는 동작인지 잘 생각해 사용해 봅시다.

    요약



    Proxy와 Reflect는 세트로 기억해 둡시다. get/set에 한정되지 않고, 양자의 API는 통일되고 있어 조합해 사용하도록(듯이) 설계되고 있습니다.

    "this가 무엇을 가리키는가?"는 JavaScript를 작성할 때 항상 의식해야 할 포인트입니다. getter/setter라는 비교적 새로운 요소에 대해서도 잊지 마세요.

    참고 URL



    ES2015(ES6)


  • 9.5 Proxy Object Internal Methods and Internal Slots
  • 26.1 The Reflect Object
  • 9.1.8 [[Get]] (P, Receiver)

  • ES2016(ES7)


  • 9.5 Proxy Object Internal Methods and Internal Slots
  • 26 Reflection
  • 9.1.8.1 OrdinaryGet (O, P, Receiver)

  • ES Wiki


  • h tp // 우우키. 에c마 sc리 pt. 오 rg / 두쿠. php? 아니 d = 하 r도 ny : ぢ ct_p
  • h tp // 우우키. 에c마 sc리 pt. 오 rg / 두쿠. php? 이 d = 는 r도 ny :
  • 좋은 웹페이지 즐겨찾기