2023.06.16
function example(a, b, c) {
console.log(`this.name = ${this.name}, a = ${a}, b = ${b}, c = ${c}`);
}
const thisArg = { name: 'test' };
const args = [1, 2, 3];
Reflect.apply(example, thisArg, args); // this.name = test, a = 1, b, = 2, c = 3
// Reflect.apply에 대한 호출을 example.apply에 대한 호출로 바꿀 수 있다.
example.apply(thisArg, args); // this.name = test, a = 1, b, = 2, c = 3
// ES5에서
var o = Thing.apply(Object.create(Thing.prototype), theArguments);
// ES2015+에서
let o = new Thing(theArguments);
// 또는
let o = Reflect.construct(Thing, theArguments);
// Defining the function that builds custom errors
function buildCustomError(...args) {
return Reflect.construct(Error, args, buildCustomError);
}
buildCustomError.prototype = Object.assign(Object.create(Error.prototype), {
constructor: buildCustomError,
report() {
console.log(`this.message = ${this.message}`);
},
});
// Using it
const e = buildCustomError('error message here');
console.log('instanceof Error', e instanceof Error);
e.report();
console.log(e);
class Product {
constructor(x, y) {
this.x = x;
this.y = y;
}
get result() {
return this.x * this.y;
}
}
class DoubleProduct extends Product {
get result() {
return super.result * 2;
}
}
const d = new DoubleProduct(10, 2);
console.log(d.result); // 40
get result() {
const proto = Object.getPrototypeOf(Object.getPrototypeOf(this));
const descriptor = Object.getOwnPropertyDescriptor(proto, "result");
const superResult = descriptor.get.call(this);
return superResult * 2;
}
// Reflect.get을 사용하면 다음 코드처럼 훨씬 더 간단해진다.
get result() {
const proto = Object.getPrototypeOf(Object.getPrototypeOf(this));
return Reflect.get(proto, "result", this) * 2
}
value = Reflect.get(target, propertyNamel, receiver]);
result = Reflect.set(target, propertyName, valuel, receiver]);
| 함수 | 설명 |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| defineProperty | Object.defineProperty와 비슷하지만 오류가 발생하는 대신 성공하면 true를, 실패하면 false를 반환한다. |
| deleteProperty | delete 연산자의 함수 버전이다. 단 엄격 모드에서도 항상 성공의 경우 true를 반 삭제 속성 환하고 실패의 경우 false를 반환한다. |
| getOwnPropertyDescriptor | Object.getOwnPropertyDescriptor 와 유사하지만 객체가 아닌 것을 전달하는 경우(강제 적용이 아닌) 예외가 발생한다는 점이 다르다. |
| getPrototypeof | Object.getPrototypeOf와 유사하지만, 객체가 아닌 것을 전달하는 경우 강제적 용이 아닌 예외가 발생한다는 점이 다르다. |
| has | in 연산자의 함수 버전(hasOwnProperty와 다음, has는 프로토타입도 검사함)이다. |
| isExtensible | Object.isExtensible과 유사하지만 false를 반환하는 대신 객체가 아닌 것을 전달하면 예외가 발생한다. |
| preventExtentions | Object.preventExtensions와 유사하지만 1) 객체가 아닌 것을 전달하면 아무것도 하지 않고 해당 값을 반환하는 대신 오류가 발생한다. 2) 작업이 실패하면 (오류를 던지지 않고) false를 반환한다. |
| setPrototypeOf | Object.setPrototypeOf와 유사하지만 1) 객체가 아닌 것을 전달하면 아무것도 하지 않고 해당 값을 반환하는 대신 오류가 발생한다. 2) 작업이 실패하면 (오류를 던지지 않고) false를 반환한다. |
const obj = {
testing: "abc",
};
const p = new Proxy(obj, {
get(target, name, receiver) {
console.log(`(getting property '${name}')`);
return Reflect.get(target, name, receiver);
},
});
console.log("Getting 'testing' directly...");
console.log(`Got ${obj.testing}`);
console.log("Getting 'testing' via proxy...");
console.log(`Got ${p.testing}`);
console.log("Getting non-existent property 'foo' via proxy...");
console.log(`Got ${p.foo}`);
| 트랩 이름 | 작업 이름 | 발생 시점 |
| ------------------------ | --------------------- | ---------------------------------------------------------------------------------------- |
| apply | [[Call]] | 프록시가 함수로 함수 호출될 때(프록싱 함수일 때만 사용 가능) |
| construct | [[Construct]] | 프록시가 생성자로 사용될 때(생성자를 프록시할 때만 사용 가능) |
| defineProperty | [[DefineOwnProperty]] | 속성이 프록시에서 정의 또는 재정의될 때(데이터 속성인 경우 해당 값이 설정되는 경우 포함) |
| deleteProperty | [[Delete]] | 프룩시에서 속성이 삭제될 때 |
| get | [[Get]] | 속성 값을 프록시에서 읽어질 때 |
| getOwnPropertyDescriptor | [[GetOwnProperty]] | 속성의 설명자가 프록시에서 읽어질 때(예상보다 할씬 더 자주 발생함) |
| getPrototype0f | [[GetPrototypeOt]] | 프록시의 프로토타입을 읽어질 때 |
| has | [[HasProperty]] | 프록시를 통해 속성의 존재 여부를 확인될 때(예. in 연산자 또는 이와 유사한 사용) |
| isExtensible | [[IsExtensible]] | 프로시가 확장 가능한지 확인하기 위해 검사될 때(즉, 아직 확장이 방지되지 않았음) |
| ownKeys | [[OwnPropertyKeys]] | 프록시의 고유한 속성 이름을 읽어질 때 |
| preventExtensions | [[PreventExtensions]] | 프룩시에서 확장이 방지될 때 |
| set | [[Set]] | 속성 값이 프록시에서 설정될 때 |
| setPrototype0f | [[SetPrototypeOf]] | 프록시의 프로토타입이 설정될 때 |
const obj = {};
const p = new Proxy(obj, {
defineProperty(target, propName, descriptor) {
if ('writable' in descriptor && !descriptor.writable) {
const currentDescriptor = Reflect.getOwnPropertyDescriptor(target, propName);
if (currentDescriptor && currentDescriptor.writable) {
return false;
}
}
return Reflect.defineProperty(target, propName, descriptor);
},
});
p.a = 1;
console.log(`p.a = ${p.a}`);
console.log('Trying to make p.a non-writable...');
console.log(
`Result of defineProperty: ${Reflect.defineProperty(p, 'a', {
writable: false,
})}`,
);
console.log('Setting pa.a to 2...');
p.a = 2;
console.log(`p.a = ${p.a}`);
const obj = {};
const p = new Proxy(obj, {
defineProperty(target, propName, descriptor) {
if ('writable' in descriptor && !descriptor.writable) {
const currentDescriptor = Reflect.getOwnPropertyDescriptor(target, propName);
if (currentDescriptor && currentDescriptor.writable) {
return false;
}
}
return Reflect.defineProperty(target, propName, descriptor);
},
});
p.a = 1;
console.log(`p.a = ${p.a}`);
console.log('Trying to make p.a non-writable...');
console.log(
`Result of defineProperty: ${Reflect.defineProperty(p, 'a', {
writable: false,
})}`,
);
console.log('Setting pa.a to 2...');
p.a = 2;
console.log(`p.a = ${p.a}`);
const proto = {
testing: 'one two three',
};
const obj = Object.create(proto);
const p = new Proxy(obj, {
getPrototypeOf(target) {
return null;
},
});
console.log(p.testing); // one two three
console.log(Object.getPrototypeOf(p)); // null
const obj = {};
const p = new Proxy(obj, {
preventExtensions(target) {
return false;
},
});
console.log(Reflect.isExtensible(p)); // true
console.log(Reflect.preventExtensions(p)); // false
console.log(Reflect.isExtensible(p)); // true
const obj = { foo: 42 };
const p = new Proxy(obj, {
setPrototypeOf(target, newProto) {
// Return false unless `newProto` is already `target`'s prototype
return Reflect.getPrototypeOf(target) === newProto;
},
});
console.log(Reflect.getPrototypeOf(p) === Object.prototype); // true
console.log(Reflect.setPrototypeOf(p, Object.prototype)); // true
console.log(Reflect.setPrototypeOf(p, Array.prototype)); // false
class Counter {
constructor(name) {
this._value = 0;
this.name = name;
}
increment() {
return ++this._value;
}
get value() {
return this._value;
}
}
function getCounter(name) {
const proxies = new WeakMap();
const p = new Proxy(new Counter(name), {
get(target, name, receiver) {
if (name === '_value') {
return undefined;
}
let value = Reflect.get(target, name);
if (typeof value === 'function') {
let funcProxy = proxies.get(value);
if (!funcProxy) {
funcProxy = new Proxy(value, {
apply(funcTarget, thisValue, args) {
const t = thisValue === p ? target : thisValue;
return Reflect.apply(funcTarget, t, args);
},
});
proxies.set(value, funcProxy);
value = funcProxy;
}
}
return value;
},
getOwnPropertyDescriptor(target, propName) {
if (name === '_value') {
return undefined;
}
return Reflect.getOwnPropertyDescriptor(target, propName);
},
defineProperty(target, name, descriptor) {
if (name === '_value') {
return false;
}
return Reflect.defineProperty(target, name, descriptor);
},
has(target, name) {
if (name === '_value') {
return false;
}
return Reflect.has(target, name);
},
ownKeys(target) {
return Reflect.ownKeys(target).filter((key) => key !== '_value');
},
});
return p;
}
const p = getCounter('p');
console.log('p.value before increment:');
console.log(p.value); // 0
console.log('p._value before increment:');
console.log(p._value); // undefined
const { increment } = Object.getPrototypeOf(p);
increment.call(p);
// => Throws TypeError: 'defineProperty' on proxy: trap returned falsish...
console.log('p.value after increment:');
console.log(p.value);
console.log('p._value after increment:');
console.log(p._value);
console.log("'_value' in p:");
console.log('_value' in p);
console.log('Object.keys(p):');
console.log(Object.keys(p));
p._value = 42;
console.log('p.value after changing _value:');
console.log(p.value);
const obj = { answer: 42 };
const { proxy, revoke } = Proxy.revocable(obj, {});
console.log(proxy.answer); // 42
revoke();
console.log(proxy.answer); // TypeError: Cannot perform 'get' on
// a proxy that has been revoked