Reflect?
reflecting이란 코드가 구동시(런타임) 자기 자신을 검사, 수정할 수 있는 능력을 말한다.
위키피디아에서 확인해보면, 언어별 reflect 방법을 소개한 것을 확인할 수 있다.
Reflect라는 영단어의 본래 의미를 생각해본다면, "자기 자신을 반추하다" 정도로 이해할 수 있을 것 같다.
reflection programming is the ability of a process to examine, introspect, and modify its own structure and behavior.[
출처 : https://en.wikipedia.org/wiki/Reflective_programming
vanilla javascript에서도 Reflect는 기본 내장 객체로 존재한다.
이 기능이 부족해서 decorator와 함께 쓰이는 reflect-metadata pollyfill로 강화하여 사용하기도 한다.
자세하게는 아래 Decorator 포스트를 살펴보자.
여기서는 Reflect와 Proxy에 대해 알아보도록하자
1. Reflect 표준 내장 객체
developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Reflect
* 다른 대부분의 전역 객체와 다르게, Reflect는 생성자가 아닙니다. 따라서 함수처럼 호출하거나 new 연산자로 인스턴스를 만들 수 없습니다. Math 객체처럼, Reflect의 모든 속성과 메서드는 정적(static)입니다.
* Object 객체에도 있는 메서드들이 보이는데, Reflect가 예전에 존재해왔던 메서드들은 묶은 것이기 때문입니다.
* IE 만 아니면 브라우저에서 다 실행 가능합니다.
* TS 환경에서 Reflect를 사용하려면 target을 es6 이상으로 설정하시거나(lib로 dom, es6, dom.iterable, scripthost을 사용함.) lib에 "DOM.Iterable"을 추가하셔야 합니다.
간단한 사용법
Reflect.has(obj, key)
특정 객체가 어떤 key 값을 가지고 있는지
const duck = {
name: "Maurice",
color: "white",
greeting: function () {
console.log(`Quaaaack! My name is ${this.name}`);
},
};
Reflect.has(duck, "color"); // true
Reflect.has(duck, "haircut"); // false
Reflect.ownKeys(obj)
객체의 키를 배열로 만들기(Object.keys와 같음)
const duck = {
name: "Maurice",
color: "white",
greeting: function () {
console.log(`Quaaaack! My name is ${this.name}`);
},
};
Reflect.ownKeys(duck); // [ "name", "color", "greeting" ]
Object.keys(duck); // [ "name", "color", "greeting" ]
Reflect.set(obj, key, value)
키:값 전달
const duck = {
name: "Maurice",
color: "white",
greeting: function () {
console.log(`Quaaaack! My name is ${this.name}`);
},
};
Reflect.set(duck, "eyes", "black");
Reflect.set(duck, "color", "black"); // overwrite가 됨
한 눈에 보는 예시들.
const a = {
name: 'darren',
job: 'writer',
num: 1.75,
};
console.log(Reflect.has(a, 'name')); // true/false
console.log(Reflect.get(a, 'name')); // darren
console.log(Reflect.set(a, 'Date', new Date())); // true/false (성공 여부)
console.log(Reflect.ownKeys(a)); // Object.keys와 같은 결과
console.log(Reflect.apply(Math.floor, undefined, [a.num])); // 1.75에 Math.floore 적용. ArrayLike를 받음
이 외에도 많은 메서드가 있지만 필요할 때마다 아래 포스트를 참고하자.
www.zerocho.com/category/ECMAScript/post/57ce7fec2a00e600151f085c
정의 부분은 아래 같이 생겼으니 이것을 참고해도 좋다.
declare namespace Reflect {
function apply(target: Function, thisArgument: any, argumentsList: ArrayLike<any>): any;
function construct(target: Function, argumentsList: ArrayLike<any>, newTarget?: any): any;
function defineProperty(target: object, propertyKey: PropertyKey, attributes: PropertyDescriptor): boolean;
function deleteProperty(target: object, propertyKey: PropertyKey): boolean;
function get(target: object, propertyKey: PropertyKey, receiver?: any): any;
function getOwnPropertyDescriptor(target: object, propertyKey: PropertyKey): PropertyDescriptor | undefined;
function getPrototypeOf(target: object): object;
function has(target: object, propertyKey: PropertyKey): boolean;
function isExtensible(target: object): boolean;
function ownKeys(target: object): PropertyKey[];
function preventExtensions(target: object): boolean;
function set(target: object, propertyKey: PropertyKey, value: any, receiver?: any): boolean;
function setPrototypeOf(target: object, proto: any): boolean;
}
Object보다 Reflect가 더 자연스러워서 Object 대신 사용하는 경우도 많다고 합니다.
2. Proxy 표준 내장 객체
developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Proxy
MDN의 설명에 따르면 Proxy 객체는 기본적인 동작(속성 접근, 할당, 순회, 열거, 함수 호출 등)의 새로운 행동을 정의할 때 사용한다고 한다.
아! 그렇다면, Object가 가진 기본적인 동작들을 재정의하는데 사용한다고 이해하시면 될 것 같습니다.
그래서 Proxy란 이름이 붙은 거라고 유추할 수 있겠습니다. 기본 동작을 하기 전에 앞단에서 인터셉트해서 다른 동작을 만들어 내는 것이죠.
new Proxy(target, handler);
handler에 상응하는 트랩이 있으면 트랩이 실행되어 프락시가 해당 작업을 처리하고 트랩이 없으면 작업은 target에 직접 수행됩니다.
말로 하니까 이해가 안되죠? 직접 예시를 보면서 작업해봅시다.
const me = {
name: 'darren',
job: 'programmer',
age: 120,
};
const proxiedMe = new Proxy(me, {
get: function () {
console.log('저 proxy입니다.');
return "haha no you can't";
},
});
console.log(proxiedMe.name); // 저 proxy입니다. haha no you can't
console.log(proxiedMe.job); // 저 proxy입니다. haha no you can't
console.log(proxiedMe.age); // 저 proxy입니다. haha no you can't
handler 부분에서 get은 프로퍼티를 읽을 때 작동하는 내부 메서드입니다. Proxy를 거친 후의 객체인 proxiedMe에서 어떤 프로퍼티를 읽더라도 handler에 작성한 trap이 발동되어서 다른 값을 호출하게 되는 것이죠.
🚨 추측할 수 있듯, Proxy를 씌운 객체가 있다면, 본래 객체를 바꾸는 일은 자제해야 합니다. 코드의 동작을 예측하기가 어려워지기 때문입니다.
이렇게 교체 가능한 내부 메서드들은 다음과 같은 값들이 존재합니다.
조금 생각해보면, 인터셉팅하는 Proxt를 이용해서 여러가지 재미있는 일들을 해볼 수 있을 것 같습니다.
대표적으로, 어떤 값에 대한 validation을 Proxy를 거쳐서 한다던가, 조건에 따라 다른 값을 출력하는 그런 일들이요
아래와 같은 코드들로, get할 때마다 numberOption을 체크하는 로직을 넣어줄 수 있습니다.
const numberOption = [0, 1, 2, 3];
// 재할당하기 위해서 let을 사용합시다. 원본 객체를 이용할 수 있으면 오히려 더 혼란을 줍니다.
let order = {
option: 6,
product: '네스프레소',
};
order = new Proxy(order, {
get: function (target, property) {
console.log(target); // proxy할 객체, 즉, target. { option: 6, product: '네스프레소' }
console.log(property); // get할 property 이름.
if (property === 'option') {
if (property in numberOption) {
return property;
} else {
return 'invalide Option';
}
}
return property;
},
});
console.log(order.option); // 'invalide Option'
그 외의 다른 프로퍼티들을 덮어쓰고 활용하는 방법에 대해서는 ko.javascript.info/proxy#ref-559 를 참고해봅시다.
참고하면 좋은 글)
아 좋은 글이 너무 많네요.
ui.toast.com/weekly-pick/ko_20210413
medium.com/intrinsic/proxies-and-reflection-in-javascript-334412028f69
'Programming Language > 🟨 Javascript (Core)' 카테고리의 다른 글
javascript 모듈 시스템 : CJS, AMD, UMD, ESM (0) | 2021.05.30 |
---|---|
iteration : iterable(순회할 수 있는 객체)와 iterator(순환체) (0) | 2021.05.23 |
TS로 살펴보는 Promise + Promise.all 병렬처리 (0) | 2020.08.25 |
Symbol 타입의 변수 (0) | 2020.07.15 |
javascript의 가비지 컬렉션 (0) | 2020.05.27 |