Javascript Proxy 와 Reflect 간단 정리하기
최근 Proxy 와 Reflect 을 알게되어 간단히 정리해보고자 한다.
Javascript 에서 Proxy 는 객체를 Wrapping 하고 객체의 동작을 가로채 수정하거나, 추가 행동을 할 수 있게 해준다.
간단 예제
function Obj(value) { this.value = value }
Obj.prototype.prototype_value = 20var origin_object = new Obj(10)
var proxy_object = new Proxy(origin_object, {
get: function (target, key, receiver) {
// target === origin_object
// key : 호출한 key
// receiver === proxy_object
if(key === 'proxy_value') {
return 30
}
return Reflect.get(target, key, receiver);
},
set: function(target, key, value, receiver) {
if(key === 'proxy_value') {
console.log(target, key, value, receiver)
return true
}
return Reflect.set(target, key, value, receiver);
}
});proxy_object.proxy_value = 40console.log(
origin_object.value, // 10
origin_object.prototype_value, // 20
origin_object.proxy_value, // undefined proxy_object.value, // 10
proxy_object.prototype_value, // 20
proxy_object.proxy_value, // 30
)
- Obj 객체를 생성 합니다.
- Proxy 로 생성한 Obj 를 Wrapping 합니다.
- proxy_object 의 proxy_value 를 호출합니다.
- 이때 Proxy 객체를 생성할때, 선언한 handler 에 get 함수가 있었기 때문에, 해당 proxy_object 의 프로퍼티를 호출 할때, handler 의 get 함수를 먼저 호출합니다.
- 만약 Proxy handler 의 반환자로 Reflect 를 반환했다면, origin_object 객체를 탐색합니다.
Proxy 에서 가로챌수 있는 항목들
Property
- get
- set
- has
- deleteProperty
Method
- apply
- construct
Object
- getPrototypeOf
- setPrototypeOf
- isExtensible
- preventExtensions
- getOwnPropertyDescriptor
- defineProperty
- ownKeys
Javascript 에서의 Object 는 어떻게 동작할까
Javascript 에서 Object 는 프로퍼티로 구성되어 있으며, key 와 value 형태로 구성되어 있다.
각 프로퍼티는 object.key 혹은 object[“key”] 형태로 접근할 수 있다.
Object 의 프로퍼티에 접근 하게되면 [[Get]]
연산을 진행한다
[[Get]]
연산은 주어진 이름의 프로퍼티를 찾아보고 있으면, 그 값을 반환한다
만약 해당 객체의 프로퍼티가 없다면, [[Get]]
연산은 [[Prototype]]
링크를 따라 일치하는 프로퍼티 명이 나올 때까지 연쇄 과정을 반복하게 된다.
Object.prototype 까지 찾아가도 없다면, undefined 를 반환한다.
Object.prototype.a = 1var obj = {
get b() { return 2 }
}Object.defineProperty(obj, 'c', {
get: function() { return 3 },
enumerable: false
})console.log(
obj.a, // 1
obj.b, // 2
obj.c, // 3
)
Javascript Proxy 와 Reflect
Javascript Proxy 는 Handler 에서 지정한 연산자중에 Target Object 에서 발생한 행동을 가로채서 추가행동을 이어갈 수 있다.
Target Object 에서 [[Get]]
연산이 발생하면, Proxy Handler 에 정의한 get 함수가 먼저 호출하게 된다.
때문에, get 함수에 정의한 내용에 따라 Target Object 의 [[Get]]
연산과 별개로 동작이 가능하다.
만약 Target Object 의 [[Get]]
연산을 이어나가고 싶다면, Reflect.get 으로 처리가 가능하다
만약 Handler 에 get 을 따로 정의하지 않았다면, Target Object 의 [[Get]]
연산을 처리한다.
Proxy API 는 object 뿐만 아니라 function 의 행동 또한 가로챌 수 있다.
function 을 Proxy 할때는 apply(target, thisArg, args) 를 사용할 수 있다.
function Fn() { console.log('Fn') }var fn = new Proxy(Fn, {
apply: function(target, thisArg, argumentsList) {
if (argumentsList.length) {
console.log(...argumentsList)
}
return Reflect.apply(target, thisArg, argumentsList)
}
});fn()
// Fnfn('a', 'b')
// a b
Reflect 는 Proxy 와 동일한 Method 를 가지고 있으며, Proxy 내에서 Reflect 를 반환하면 사이드 이펙트 없이 사용이 가능하다.
또한 반드시 Proxy 내부에서만 사용할 필요없이 따로 사용이 가능하다.
Reflect.get({a: 10}, 'a')
// 10
Proxy.revocable
Proxy.revocable 는 Target Object 를 참조하던 Proxy 객체를 해제할 수 있는 revoke 를 추가적으로 제공한다.
Proxy 객체의 Target Object 는 원본이 null 이 되더라도 반영되지 않는다.
때문에, revoke 를 이용하면 명시적으로 제거가 가능하다.
var object = {
data: null
};let {proxy, revoke} = Proxy.revocable(object, {
get: function (target, key, receiver) {
return Reflect.get(target, key, receiver);
},
set: function (target, key, value, receiver) {
return Reflect.set(target, key, value, receiver);
}
});proxy.data = 10console.log(
object.data, // 10
proxy.data, // 10
)object = nullconsole.log(
proxy.data, // 10
)try {
// Proxy 해제
revoke() proxy.data = 20
// Cannot perform 'set' on a proxy that has been revoked
} catch (err) {
console.error(err.message)
}
Proxy Polyfill
Proxy API 는 ES6 SPEC 이기 때문에, 최신 브라우저에서는 Proxy API 가 잘 지원하지만, ES5 이하를 지원하는 브라우저는 사용이 어렵다.
때문에 Babel transfer 만으로는 지원이 어렵다.
하지만 Google 팀에서 proxy-fill 만든 도구가 있으며, IE 9 까지 지원한다
모든 기능을 지원하지는 않으며 get, set, apply, construct 를 지원한다