proxy와 reflect
Proxy 와 Reflect
Proxy
는 특정 객체의 행동을 가로챔. 직접 처리하기도 하고 넘겨 주기도 하는 방식으로 동작.
Proxy
1 | let proxy = new Proxy(target, handler); |
target
감싸게 될 객체handler
동작을 가로채는 메서드인 트랩이 담긴 객체
타겟을 처리할 트랩이 있으면 트랩이 실행하고 아니면 target 이 직접 수행함.
1 | let target = {}; |
트랩이 없기 때문에 다음과 같이 동작한다.
- proxy.test = 하면 target에 값이 설정된다.
- proxy.test 를 이용해 값을 읽으면 target 에서 값을 읽어온다.
- proxy를 대상으로 반복을 수행하면 target에 지정한 값이 반환된다.
핸들러가 비어있으면 모든 작업은 target에서 수행된다.
객체의 어떤 작업을 할때는 코드로 확인할 수 없는 내부 메서드를 이용하여 수행한다. 프록시를 사용하면 이 내부 메서드를 가로챈다.
new Proxy
의 handler
에 매개변수로 추가할 수 있는 메서드는 아래와 같다.
내부 메서드나 트랩을 쓸 땐 자바스크립트에서 정한 몇 가지 규칙(invariant)를 반드시 따라야 함.
get 트랩으로 프로퍼티 기본값 설정하기
프로퍼티 읽기를 가로채려면 handler
에 get(target, property, receiver)
메서드가 있어야 한다.
- target: 동작을 전달할 객체로
new Proxy
dml 첫번째 인자이다. - property: 프로퍼티 이름
- receiver: 타깃 프로퍼티가 getter 라면
receiver
는 getter가 호출될 때this
이다.
1 | let numbers = [0, 1, 2]; |
프락시로 객체를 감쌌다면 target 객체에 접근할 수 없도록 해야 함. 위에서 number를 덮어씌워 줌으로서 원래의 numbers의 접근하지 못하도록 함.
set 트랩으로 프로퍼티 값 검증하기
set 메서드의 인수는 아래와 같은 역활을 한다.
set(target, property, value, receiver)
:
- target: 동작을 전달할 객체로
new Proxy
의 첫 번째 인자이다. - property: 프로퍼티 이름
- value: 프로퍼티 값
- receiver: get 트랩과 유사하게 동작하는 객체로, setter 프로퍼티에만 관여한다.
number 일때만 setter 되는 프록시 만들기
1 | let numbers = []; |
배열관련 기능들도 여전히 사용할 수 있다.
true를 반환하는 것을 잊지 말아야 한다.
ownKeys와 getOwnPropertyDescriptor로 반복 작업하기
Object.keys
, for..in
반복문을 비로한 프로퍼티 순환 관련 메서드 대다수는 내부 메서드 [[OwnPropertyKeys]]
(트랩 메서드는 (ownKeys
)) 를 사용해 프로퍼티 목록을 얻는다.
_
로 시작하는 프로퍼티는 for..in
반복문의 순환 대상에서 제외하도록 만듦.
1 | let user { |
Object.keys
는 enumerable
플래그가 있는 프로퍼티만 반환한다. ownKeys
에서 enumerable
플래그가 없는 객체를 반환했다면, Object.keys
를 호출해도 빈 문자열이 반환된다.
이럴땐 getOwnPropertyDescriptor
를 사용하면 된다.
1 | let user = {}; |
deleteProperty 와 여러 트랩을 사용해 프로퍼티 보호하기
_
가 붙은 프로퍼티는 내부에서만 사용하는 프로퍼티를 의미한다. 외부에서 접근해서는 안되는데 기술적으로 접근 가능한 문제가 있다.
프록시를 이요해서 접근하지 못하도록 막아 본다.
- get: 프로퍼티를 읽으려고 하면 에러를 던져줌
- set: 포로퍼티에 쓰려고 하면 에러를 던져줌
- deleteProperty: 프로퍼티를 지우려고 하면 에러를 던져줌
- ownKeys:
for..in
이나Object.keys
같은 프로퍼티 순환 메서드를 사용할 때_
로 시작하는 메서드는 제외함.
1 | let user = { |
함수를 반환할 때 함수를 target으로 바인딩 해주고 있음을 주목해야 한다. 그냥 사용하려고 프록시 객체로 사용하면 원본 target 객체 메서드에서 내부 변수로 접근할때 this 는 프록시기 때문에 접근이 거부된다.
이 함수에 this를 target 원본으로 바인딩 함으로써 문제를 해결할 수 있다. 하지만 프록시로 여러번 쌓여 있다던지 하면 문제가 또 생긴다. 프록시마다 객체에 가하는 수정이 달라지는 문제도 있다..
따라서 이런 형태의 프록시는 사용하면 안된다.
has 트랩으로 ‘범위’내 여부 확인하기
1 | let range = { |
in
연산자를 사용해 특정 숫자가 range
내에 있는지 확인해 본다. has
트랩은 in
호출을 가로챈다.
has(target, property)
- target: new Proxy의 첫 번째 인자로 전달되는 타깃 갳게
- property: 프로퍼티 이름.
1 | let range = { |
apply 랩으로 함수 감싸기
apply(target, thisArg, args)
트랩은 프락시를 함수처럼 호출하려고 할때 동작한다.
- target: 타깃 객체
- thisArg: this의 값
- args: 인수 목록
일반 함수를 사용하는 데코레이터로 예시를 들어본다.
1 | function delay(f, ms) { |
래퍼함수로 감싸고 나면 기존 함수의 프로퍼티 (name, length등 ) 정보가 사라짐.
1 | function delay(f, ms) { |
Proxy
객체는 타깃 객체에 모든 것을 전달해주므로 훨씬 강력하다.
1 | function delay(f, ms) { |