JavaScript_proxy-and-reflect-2.md
๐กJavascript Study
๋ชจ๋ ์๋ฐ์คํฌ๋ฆฝํธ Deep Dive + Mozilla
๐ Proxy & Reflect: part2
์๋ฐ์คํฌ๋ฆฝํธ https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Proxy#%ED%94%84%EB%9D%BC%EC%9D%B4%EB%B9%97_%EC%86%8D%EC%84%B1_%ED%8F%AC%EC%9B%8C%EB%94%A9_%EC%97%86%EC%9D%8C
https://replit.com/@dusunax/javascript#index.js
- Proxy ์ข ๋ ์์๋ณด๊ธฐ
Proxy ์์ ์ดํด๋ณด๊ธฐ-1
๊ธฐ๋ณธ ํ๋ก์
const handler = {
get(obj, prop) {
return prop in obj ? obj[prop] : 37; // ํค๊ฐ ์์ผ๋ฉด ๊ฐ์ ๋ฐํ, ์๋๋ฉด 37 ๋ฐํ
},
};
const p = new Proxy(
{
a: 1,
b: undefined,
},
handler
);
// p.a = 1;
// p.b = undefined;
console.log(p.a, p.b);
// 1, undefined
console.log("c" in p, p.c);
// false, 37
No-op ํฌ์๋ฉ ํ๋ก์
- ํ๋ก์๊ฐ target ๊ฐ์ฒด์๊ฒ ์์ ์ ๋ฌ
const targetEmpty = {};
const fp = new Proxy(targetEmpty, {});
fp.a = 37;
// ๋์ ๊ฐ์ฒด์๊ฒ ์์
์ ๋ฌ
console.log(targetEmpty.a); // 37
console.log(fp); // { a: 37 }
console.log(fp.a); // 37
๊ฐ์ฒด ํ๋ผ์ด๋น ์์ฑ์ ์ง์ ์ ๊ทผX
- ํ๋ก์ โ ๋ค๋ฅธ ID๋ฅผ ๊ฐ์ง ๊ฐ์ฒด, ๋ํ๋ ๊ฐ์ฒด์ ์ธ๋ถ ์ฌ์ด์์ ์๋
class Secret {
#secret; // private
constructor(secret) {
this.#secret = secret;
}
get secret() {
return this.#secret.replace(/\d+/, "[REDACTED]");
}
}
const aSecret = new Secret("์ ํ๋ฒํธ 1234567");
console.log(aSecret.secret); // [REDACTED]
// no-op ํฌ์๋ฉ ๊ฐ์ง๋ง...
const proxy = new Proxy(aSecret, {});
console.log(proxy.secret); // TypeError: Cannot read private member #secret from an object whose class did not declare it
- get()์ด ํธ์ถ๋ ๋, this๊ฐ์ด secret์ด ์๋๋ผ proxy์ด๋ฏ๋ก #secret์ ์ก์ธ์คํ ์ ์์
- secret์ this๋ก ์ฌ์ฉํ ๊ฒ
๐ก get trap
get
ํธ๋ฉ์return target[prop]
๊ณผ ๊ฐ์ด ์๋ ๊ฐ์ฒด์ ํ๋กํผํฐ ๊ฐ์ ๋ฐํํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์ ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉดProxy
๊ฐ์ฒด๊ฐ ์๋ ๊ฐ์ฒด์ ํ๋์ ๋ณด๊ฐํ๊ฑฐ๋ ๋ณ๊ฒฝํ ์ ์์ผ๋ฉด์๋, ๊ธฐ์กด์ ๊ฐ์ฒด ๋์์ ๊ทธ๋๋ก ํ์ฉํ ์ ์์ต๋๋ค.
const proxyS = new Proxy(aSecret, {
get(target, prop) {
// 'this' == Reflect.get(target, prop, receiver)
return target[prop];
},
});
- ๋ฉ์๋์ this๊ฐ ์๋ ๊ฐ์ฒด๋ก ๋ฆฌ๋๋ ์
class Secret4 {
#x = 1;
x() {
return this.#x;
}
}
const aSecret4 = new Secret4(); // target
const proxy4 = new Proxy(aSecret4, {
get(target, prop, receiver) {
const value = target[prop];
if (value instanceof Function) {
// instance๊ฐ ํจ์์ธ ๊ฒฝ์ฐ
return function (...args) {
// ํจ์ ๋ํ
return value.apply(this === receiver ? target : this, args); // ๊ธฐ์กด ๋์์ ๊ฐ๋ก์ฑ
// ๋ง์ฝ this๊ฐ receiver์ ๊ฐ๋ค๋ฉด(this === receiver๊ฐ ์ฐธ์ด๋ผ๋ฉด), target ๊ฐ์ฒด๋ก ์ค์
// ๊ทธ๋ ์ง ์์ผ๋ฉด ํ์ฌ ๊ฐ์ฒด(this)๋ก ์ค์
};
}
return value;
},
});
console.log(proxy4.x());
- ์ผ๋ถ js ๊ฐ์ฒด์๋ ์ก์ธ์ค ํ ์ ์๋ ๋ด๋ถ ์ฌ๋กฏ์ด ์์ ex) Map์ [[MapData]]
- ๊ฒ์ฆ: ๊ฐ์ฒด์ ์ ๋ฌ๋ ๊ฐ ํ์ธ ๊ฐ๋ฅ
const validator = {
set(obj, prop, value) {
if (prop === "age") {
// age property์ผ ๋
if (!Number.isInteger(value)) {
// ์ ์ ์๋
throw new TypeError("The age is not an integer");
}
if (value > 200) {
// ์ฌ๋ ๋์ด ์๋
throw new RangeError("The age seems invalid");
}
}
// ๊ฐ์ ์ ์ฅํ๋ ๊ธฐ๋ณธ ๋์
obj[prop] = value;
// ์ฑ๊ณต ํ์
return true;
},
};
const person = new Proxy({}, validator);
person.age = 100;
console.log(person.age); // 100
person.age = "young"; // ์์ธ ๋ฐ์: type error
person.age = 300; // ์์ธ ๋ฐ์: range error
์์ : ์์ฑ์ ํ์ฅํ๊ธฐ, construct(), apply()
- prototype extend: ํ๋กํ ํ์
์ extendํ ํด๋์ค ์์ฑ์
- extend ์ด๋ป๊ฒ? Proxy๋ก sup ์์ฑ์๋ฅผ ๊ฐ์ธ์ ๋์ ์ถ๊ฐ
function extend(sup, base) {
base.prototype = Object.create(sup.prototype);
base.prototype.constructor = new Proxy(base, {
construct(target, args) {
const obj = Object.create(base.prototype);
this.apply(target, obj, args);
return obj;
},
apply(target, that, args) {
sup.apply(that, args);
base.apply(that, args);
},
});
// ํ์ฅ๋ ํด๋์ค ์์ฑ์ ํจ์ ๋ฐํ
return base.prototype.constructor;
}
const Person = function (name) {
this.name = name;
};
const Boy = extend(Person, function (name, age) {
this.age = age;
});
Boy.prototype.gender = "M"; // Boy ํด๋์ค ์์ฑ์์ ํ๋กํ ํ์
์ ๊ฐ ์ถ๊ฐ
const Peter = new Boy("Peter", 13);
// ํ๋กํผํฐ์ ๋ฉ์๋ ์ถ๋ ฅ
console.log(Person.prototype); // {}
console.log(Boy.prototype); // Person { constructor: [Function (anonymous)], gender: 'M' }
console.log(Boy.prototype.gender); // "M"
console.log(Peter.gender); // "M"
console.log(Peter.name); // "Peter"
console.log(Peter.age); // 13
- constructor function = to โ class declaration
// Person ํด๋์ค ์ ์
const Person = function (name) {
// ์ต๋ช
ํจ์, this.ํค = ๊ฐ
this.name = name;
};
construct()
: new ์ฐ์ฐ์์ ๋ํ ํธ๋ฉ (new target์ด ์ ํจํด์ผ ํจ, [[Construct]])
new Proxy(target, {
construct(target, argumentsList, newTarget) {},
// target: ๋์ ๊ฐ์ฒด, argumentsList: ์์ฑ์ ์ธ์ ๋ชฉ๋ก, newTarget ์๋ ํธ์ถ๋ ์์ฑ์
});
// ---------------------
// construct()
// ---------------------
function monster1(disposition) {
this.disposition = disposition;
console.log("๋๋ target");
}
const handler1 = {
construct(target, args) {
console.log(`Creating a ${target.name}`);
console.log(target);
// Expected output: "Creating a monster1"
return new target(...args);
},
};
// target, handler
const proxy1 = new Proxy(monster1, handler1);
console.log(proxy1 + "hi");
console.log(new proxy1("fierce").disposition);
// Expected output: "fierce"
apply()
: ํจ์ ํธ์ถ์ ๋ํ ํธ๋ฉ
var p = new Proxy(target, {
apply: function (target, thisArg, argumentsList) {},
});
// ---------------------
// apply()
// ---------------------
function multiply(a, b) {
return a * b;
}
const handler = {
apply: function (target, thisArg, argumentsList) {
console.log(`Calculate multiply than convert to rem: ${argumentsList}`);
return target(argumentsList[0], argumentsList[1]) * 0.0625;
},
};
const calcRem = new Proxy(multiply, handler);
console.log(multiply(8, 2));
// Expected output: 16
console.log(calcRem(8, 2));
// Expected output: 1
=> Proxy ์์ ์ดํด๋ณด๊ธฐ-2์์ ๊ณ์