[JS] ์๋ฐ์คํฌ๋ฆฝํธ ํ๋ก์ Proxy ๊ฐ์ฒด / Reflect
Vue.js์์ Reactive๋ก ๋ฐ์์ฑ์ ์ฃผ์
ํ ๋ฐ์ดํฐ๋ฅผ ์ฝ์๋ก ์ฐ์ด๋ณด๋ฉด ์๋์ฒ๋ผ Proxy { ... } ํํ๋ก ์ถ๋ ฅํ๋ค. Proxy๋ฅผ ์ฌ์ฉํด์ ์
๋ ฅํ ๋ฐ์ดํฐ(์ํ)๋ฅผ ํ ๋ฒ ๊ฐ์ผ ๊ฒ์ด๋ค. ์ด๋ ๊ฒ Proxy๋ฅผ ์ด์ฉํ๋ฉด ๊ฐ์ฒด ํ๋กํผํฐ์ ์ฝ๊ธฐ/์ฐ๊ธฐ ๊ฐ์ ์์
์ ์ค๊ฐ์ ๊ฐ๋ก์ฑ์ ์ํ๋ ์์
์ ์ํํ๋๋ก ํ ์ ์๋ค.

์ฌ์ฉ ๋ฐฉ๋ฒ
const proxy = new Proxy(target, handler);
- target : Proxy๋ก ๊ฐ์ ์๋ณธ ๊ฐ์ฒด. ํจ์๋ฅผ ํฌํจํ ๋ชจ๋ ๊ฐ์ฒด ๊ฐ๋ฅ
- handler :
get์ด๋set๊ฐ์ ๋์์ ๊ฐ๋ก์ฑ๋ ๋ฉ์๋(ํธ๋ฉ; trap)์ด ๋ด๊ธด ๊ฐ์ฒด
๊ฐ์ฒด๋ฅผ proxy๋ก ๊ฐ์ผ ํ, handler์ ์์ํ๋ ํธ๋ฉ(๋ฉ์๋)๊ฐ ์์ผ๋ฉด ํธ๋ฉ์ด ์คํ๋๋ค. ํธ๋ฉ์ด ์์ผ๋ฉด target์์ ์์ ์ด ์ํ๋๋ค. ์๋ ์์์์ ํธ๋ฉ(handler)์ด ๋น์ด ์๊ธฐ ๋๋ฌธ์ proxy์ ๋ํ ๋ชจ๋ ์์ ์ด target์ผ๋ก ์ ๋ฌ๋๋ค.
const user = { name: 'johan', age: 30 };
const proxy = new Proxy(user, {});
console.log(proxy); // Proxy {name: 'johan', age: 30}
proxy.test = 30; // ํ๋กํผํฐ ์ถ๊ฐ
for (const key in proxy) console.log(key); // name, age, test
โญ๏ธ ๋ด๋ถ ๋ฉ์๋ ๋์ ํธ๋ฉ
๊ฐ์ฒด์ ์ด๋ค ์์
์ ํ ๋ ์๋ฐ์คํฌ๋ฆฝํธ ๋ช
์ธ์ ์ ์๋ ๋ด๋ถ ๋ฉ์๋(Internal Method)๊ฐ ๊ด์ฌํ๋ค. ์๋ฅผ ๋ค์ด ํ๋กํผํฐ๋ฅผ ์ฝ์ ๋ [[Get]], ํ๋กํผํฐ๋ฅผ ์ธ ๋ [[Set]] ์ด๋ผ๋ ๋ด๋ถ ๋ฉ์๋๊ฐ ๊ด์ฌํ๋ค. ๋ด๋ถ ๋ฉ์๋๋ ๊ฐ๋ฐ์๊ฐ ํธ์ถํ ์ ์๋ค. Proxy์ ํธ๋ฉ์ ์ด๋ฐ ๋ด๋ถ ๋ฉ์๋์ ํธ์ถ์ ๊ฐ๋ก์ฑ๋ฉฐ, ๋ชจ๋ ๋ด๋ถ ๋ฉ์๋์ ๋์ํ๋ ํธ๋ฉ์ด ์๋ค.
| ๋ด๋ถ ๋ฉ์๋ | Proxy ํธ๋ค๋ฌ(ํธ๋ฉ) ๋ฉ์๋ | ํธ๋ค๋ฌ(ํธ๋ฉ) ์๋ ์์ |
| [[Get]] | get | ํ๋กํผํฐ ์กฐํ |
| [[Set]] | set | ํ๋กํผํฐ ์ฐ๊ธฐ |
| [[HasProperty]] | has | in ์ฐ์ฐ์ ์ฌ์ฉ์ |
| [[Delete]] | deleteProperty | delete ์ฐ์ฐ์ ์ฌ์ฉ์ |
| [[Call]] | apply | ํจ์ ํธ์ถ ์ |
| [[Construct]] | construct | new ์ฐ์ฐ์ ์ฌ์ฉ์ |
| [[GetPrototypeOf]] | getPrototypeOf | Object.getPrototypeOf |
| [[SetPrototypeOf]] | setPrototypeOf | Object.setPrototypeOf |
| [[IsExtensible]] | isExtensible | Object.isExtensible |
| [[PreventExtensions]] | preventExtensions | Object.preventExtensions |
| [[DefineOwnProperty]] | defineProperty | Object.definePropertyObject.defineProperties |
| [[GetOwnProperty]] | getOwnPropertyDescriptor | Object.getOwnPropertyDescriptorfor...in Object.keys/values/entries |
| [[OwnPropertyKeys]] | ownKeys | Object.getOwnPropertyNamesObject.getOwnPropertySymbolsfor...inObject/keys/values/entries |
Proxy ๊ท์น
- ๊ฐ ์ฐ๋ ์์
์ฑ๊ณต ์
[[Set]]๋ฉ์๋๋ ๋ฐ๋์true๋ฐํ. ์คํจ ์false๋ฐํ - ๊ฐ ์ญ์ ์์
์ฑ๊ณต ์
[[Delete]]๋ฉ์๋๋ ๋ฐ๋์true๋ฐํ. ์คํจ ์false๋ฐํ - ํ๋ฝ์ ๊ฐ์ฒด๋ฅผ ๋์์ผ๋ก
[[GetPrototypeOf]]๋ฉ์๋๊ฐ ์ ์ฉ๋๋ฉด ํ๋ฝ์ ํ๊น ๊ฐ์ฒด์[[GetPrototypeOf]]๋ฅผ ์ ์ฉํ ๊ฒ๊ณผ ๋์ผํ ๊ฐ ๋ฐํ
ํธ๋ฉ
์กฐํ — Get
๐ก get ๋ฉ์๋(ํธ๋ฉ)๋ ํ๋กํผํฐ๋ฅผ ์กฐํํ ๋ ์๋ํ๋ค.
get(target, property, receiver)
- target : ์๋ณธ ๊ฐ์ฒด.
new Proxy()์ฒซ๋ฒ์งธ ์ธ์์ ๋์ผ - property : ํ๋กํผํฐ ์ด๋ฆ
- receiver : ์ต์ด ์์
์์ฒญ์ ๋ฐ์ ๊ฐ์ฒด
๋ณดํต์proxy๊ฐ์ฒด ์์ฒด๊ฐ receiver๊ฐ ๋๋ฉฐ, Proxy ๊ฐ์ฒด๋ฅผ ์์๋ฐ์ ๊ฐ์ฒด๊ฐ ์๋ค๋ฉด ํด๋น ๊ฐ์ฒด๊ฐ receiver๊ฐ ๋๋ค. ํ๊ฒ ํ๋กํผํฐ๊ฐ getter๋ผ๋ฉด getter๋ฅผ ํธ์ถํ ์์ ์this๋ฅผ ๊ฐ๋ฆฌํจ๋ค. ์ฃผ๋ก ํ๋กํ ํ์ ์ฒด์ด๋์์ ์ต์ด๋ก ์์ ์์ฒญ์ ๋ฐ์ ๊ฐ์ฒด๋ฅผ ํ์ธํ ๋ ์ฌ์ฉํ๋ค.
์๋๋ get ํธ๋ฉ์ ์ด์ฉํด ํ๋กํผํฐ์ ๊ธฐ๋ณธ๊ฐ์ ์ค์ ํ ์์(์กด์ฌํ์ง ์๋ ํ๋กํผํฐ๋ฅผ ์กฐํํ ๋ ๊ธฐ๋ณธ๊ฐ ๋ฐํ).
// ์์ 1
let numbers = [0, 1, 2];
numbers = new Proxy(numbers, {
get(target, prop) {
// ์ฌ๊ธฐ์ ํ๋กํผํฐ๋ 0, 1, 2 ์ธ๋ฑ์ค
if (prop in target) return target[prop];
// hasOwnProperty๋ฅผ ์ฌ์ฉํ ์๋ ์๋ค ex) *if (target.hasOwnProperty(prop)) {...}*
// Reflect๋ฅผ ์ฌ์ฉํ ์๋ ์๋ค(์ถ์ฒ) *ex) if (Reflect.has(target, prop)) {...}*
return 0; // ๊ธฐ๋ณธ๊ฐ
},
});
numbers[1]; // 1
numbers[8]; // 0 (8๋ฒ ์ธ๋ฑ์ค์ ํด๋นํ๋ ์์๊ฐ ์์ผ๋ฏ๋ก 0 ๋ฐํ)
// ์์ 2
let dictionary = { Hello: '์๋
ํ์ธ์', Bye: '์๋
ํ๊ฐ์ธ์' };
dictionary = new Proxy(dictionary, {
get(target, phrase) {
// ํ๋กํผํฐ ์ฝ๊ธฐ๋ฅผ ๊ฐ๋ก์ฑ
if (phrase in target) return target[phrase];
// ๊ตฌ์ ์ด ์์ผ๋ฉด ๋ฒ์ญ๋ฌธ ๋ฐํ
return phrase; // ๊ตฌ์ ์ด ์์ผ๋ฉด ๊ตฌ์ ๊ทธ๋๋ก ๋ฐํ
},
});
dictionary['Hello']; // '์๋
ํ์ธ์'
dictionary['Good Night']; // 'Good Night' (ํด๋นํ๋ ๊ตฌ์ ์ด ์์ผ๋ฏ๋ก ๊ตฌ์ ๊ทธ๋๋ก ๋ฐํ)
๐ก Target ๊ฐ์ฒด๋ฅผ Proxy๋ก ๊ฐ์ผ ํ๋ถํด ๋ฐ๋์ Target ๊ฐ์ฒด๋ฅผ ์ง์ ์ฐธ์กฐํ๋ ์ฝ๋๊ฐ ์์ด์ผ ํ๋ค.
์ฐธ๊ณ ๋ก in ์ฐ์ฐ์๋ ๋ช
์ํ ์์ฑ์ด ๊ฐ์ฒด์ ์กด์ฌํ๋์ง ์ฌ๋ถ๋ฅผ boolean ๊ฐ์ผ๋ก ๋ฐํํ๋ค.
const arr = [1, 2, 3, 4, 5];
0 in arr; // true (0๋ฒ index๊ฐ ์กด์ฌํ๋ฏ๋ก)
5 in arr; // false (5๋ฒ index๊ฐ ์กด์ฌํ์ง ์์ผ๋ฏ๋ก)
'length' in arr; // true (๋ฐฐ์ด์ length ํ๋กํผํฐ๋ฅผ ๊ฐ์ง๋ฏ๋ก)
'concat' in arr; // true
const obj = { name: 'Smith', age: 30 };
'name' in obj; // true
'city' in obj; // false
๊ฐ์ฒด์ ํน์ ํ๋กํผํฐ๊ฐ ์๋์ง ํ์ธํ๋ hasOwnProperty ๋ฅผ ์ฌ์ฉํ๋ฉด no-prototype-builtins ESLint ๊ฒฝ๊ณ ๊ฐ ๋ฐ์ํ๋ค. ํ์ธํ๋ ค๋ ๊ฐ์ฒด์ hasOwnProperty์ ๋์ผํ ์ด๋ฆ์ ํ๋กํผํฐ๊ฐ ์์ ์๋ ์๊ธฐ ๋๋ฌธ์ด๋ค. call ๋ฉ์๋๋ก this๋ฅผ ๋ฐ์ธ๋ฉํด์ ํธ์ถํ๋ฉด ESLint ๊ฒฝ๊ณ ๋ฅผ ํผํ ์ ์์ง๋ง ์ฝ๋๊ฐ ๋๋ฌด ๊ธธ์ด์ง๋ค.
Object.prototype.hasOwnProperty.call(target, prop); // true | false
hasOwnProperty ๋์ Reflect ๊ฐ์ฒด๊ฐ ์ ๊ณตํ๋ has ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ๋ ๊น๋ํ๊ฒ ์์ฑํ ์ ์๋ค(์ฐธ๊ณ ).
Reflect.has(target, prop) // true | false
์ฐ๊ธฐ — Set
๐ก set ๋ฉ์๋(ํธ๋ฉ)๋ ํ๋กํผํฐ๋ฅผ ์ธ ๋(write) ์๋ํ๋ค.
set(target, property, value, receiver)
- target : ์๋ณธ ๊ฐ์ฒด.
new Proxy()์ฒซ๋ฒ์งธ ์ธ์์ ๋์ผ - property : ํ๋กํผํฐ ์ด๋ฆ
- value : ํ๋กํผํฐ ๊ฐ
- receiver :
getํธ๋ฉ๊ณผ ์ ์ฌํ๊ฒ ๋์ํ๋ ๊ฐ์ฒด. setter ํ๋กํผํฐ์๋ง ๊ด์ฌํจ
set์ ์ด์ฉํด ์ซ์๋ง ์ ์ฅํ ์ ์๋ ๋ฐฐ์ด์ ๋ง๋ ๋ค๊ณ ๊ฐ์ ํ๊ณ , ๋ง์ฝ ์ซ์๊ฐ ์๋ ๋ค๋ฅธ ํ์
์ ์ถ๊ฐํ๋ ค๊ณ ํ๋ฉด ์๋ฌ๋ฅผ ๋ฐ์ํ๋๋ก ํ ์ ์๋ค. value๊ฐ ์ซ์์ผ ๋๋ง true๋ฅผ ๋ฐํํ๊ณ , ์ซ์ ํ์
์ด ์๋๋ false๋ฅผ ๋ฐํํ๋๋ก ํ๋ฉด ๋๋ค.
๐ก set ํธ๋ฉ์ ์ฌ์ฉํ ๋ ํญ์ true, false๋ฅผ ๋ฐํํด์ผ ํ๋ ์ ์์ง ๋ง๊ฒ.
let numbers = [];
numbers = new Proxy(numbers, {
set(target, prop, value) {
if (typeof value === 'number') {
target[prop] = value;
return true;
}
return false;
},
});
numbers.push(1); // value๊ฐ ๋๋ฒ ํ์
์ด๋ฏ๋ก ์ถ๊ฐ ์ฑ๊ณต
numbers.push('2'); // Error! value๊ฐ ๋ฌธ์ํ์ด๋ฏ๋ก ์ถ๊ฐ ์คํจ
numbers.length; // 1
Proxy๋ก ๋ฐฐ์ด(๊ฐ์ฒด)๋ฅผ ๊ฐ์ธ๋ push๋ length ๊ฐ์ ํ๋กํผํฐ๋ฅผ ๊ณ์ ์ฌ์ฉํ ์ ์๋ค. push๋ unshift ๊ฐ์ ๋ฐฐ์ด ๋ฉ์๋๋ [[Set]]์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๋ฉ์๋๋ฅผ ๋ฎ์ด์ฐ์ง ์๊ณ ๋ Proxy๊ฐ ๋์์ ๊ฐ๋ก์ฑ ํ ๊ฐ์ ๊ฒ์ฆํ ์ ์๋ ๊ฒ.
๋ฐ๋ณต — ownKeys, getOwnPropertyDescriptor
Object.keys, for...in ๊ฐ์ ์ํ ๋ฉ์๋ ๋๋ถ๋ถ์ ๋ด๋ถ ๋ฉ์๋ [[OwnPropertyKeys]](ํธ๋ฉ ๋ฉ์๋ ownKeys)๋ฅผ ์ฌ์ฉํด์ ํ๋กํผํฐ ๋ชฉ๋ก์ ์ป๋๋ค. ์ด ์ํ ๋ฉ์๋์ ์ธ๋ถ ๋์์ ์๋์ ๊ฐ์ ์ฐจ์ด๊ฐ ์๋ค.
| intercept(ๆฆๆช) ํ๋ ๋ฉ์๋ | ์๋ ์ค๋ช |
Object.getOwnPropertyNames(obj) |
์ฌ๋ณผํ์ด ์๋ key๋ง ๋ฐํ |
Object.getOwnPropertySymbols(obj) |
์ฌ๋ณผํ key๋ง ๋ฐํ |
Object.keys/values() |
โenumerable ํ๋๊ทธ๊ฐ true๋ฉด์ ์ฌ๋ณผํ์ด ์๋ key, โ์ฌ๋ณผํ์ด ์๋ key์ ํด๋นํ๋ ๊ฐ ์ ์ฒด ๋ฐํ |
for...in |
โenumerable ํ๋๊ทธ๊ฐ true์ด๊ณ ์ฌ๋ณผํ์ด ์๋ key, โํ๋กํ ํ์
key ์ํ |
์๋๋ ownKeys ํธ๋ฉ์ ์ด์ฉํด _๋ก ์์ํ๋ ํ๋กํผํฐ๋ฅผ for...in ์ํ ๋์์์ ์ ์ธํ๋๋ก ํ ์์. Object.key/values์๋ ๋์ผํ ๋ก์ง์ด ์ ์ฉ๋๋ค. (_๋ก ์์ํ๋ ํ๋กํผํฐ๋ ์ํ ๋์์์ ์ ์ธํ๋ค)
let user = {
name: 'Johan',
age: 30,
_password: '1q2w3e4r',
};
user = new Proxy(user, {
ownKeys(target) {
// _๋ก ์์ํ๋ key๋ ๊ฑด๋๋ฐ๋๋ก ํ๋ ํธ๋ฉ
return Object.keys(target).filter((key) => !key.startsWith('_'));
},
});
for (const key in user) console.log(key); // name, age
Object.keys(user); // ['name', 'age']
Object.values(user); // ['John', 30]
์๋ ์ฝ๋๋ ์๋ํ๋๋ก ์๋ํ์ง ์๋๋ค. Object.keys๋ ๋ด๋ถ ๋ฉ์๋์ธ [[GetOwnProperty]]๋ฅผ ํธ์ถํด ๋ชจ๋ ํ๋กํผํฐ์ ์ค๋ช
์๋ฅผ ํ์ธํ ํ enumerable ํ๋๊ทธ๊ฐ ์๋ ํ๋กํผํฐ๋ง ๋ฐํํ๋ค. ์๋์์ user๋ ํ๋กํผํฐ ์ค๋ช
์๋ ์๊ณ enumerable ํ๋๊ทธ๋ ์์ผ๋ฏ๋ก ์ํ ๋์์์ ์ ์ธ๋ ๊ฒ.
let user = {};
user = new Proxy(user, {
ownKeys(target) {
return ['a', 'b', 'c'];
},
});
Object.keys(user); // ['a', 'b', 'c']๋ฅผ ์์ํ์ง๋ง [] ๋น ๋ฐฐ์ด๋ง ๋์ด
Object.keys ํธ์ถ ์ ์๋ํ๋๋ก ํ๋กํผํฐ๋ฅผ ๋ฐํํ๋๋ก ํ๋ ค๋ฉด enumerable ํ๋๊ทธ๋ฅผ ์ถ๊ฐํ๊ฑฐ๋, ๋ด๋ถ ๋ฉ์๋์ธ [[GetOwnProperty]]๊ฐ ํธ์ถ๋ ๋ ์ด๋ฅผ ๊ฐ๋ก์ฑ์ ์ค๋ช
์ enumerable: true๋ฅผ ๋ฐํํ๋๋ก ํ๋ฉด ๋๋ค. getOwnPropertyDescriptor ํธ๋ฉ์ ์ด๋ ์ฌ์ฉ๋๋ค.
let user = {};
user = new Proxy(user, {
ownKeys(target) {
// ํ๋กํผํฐ ๋ฆฌ์คํธ๋ฅผ ์ป์ ๋ 1๋ฒ ํธ์ถ๋จ
return ['a', 'b', 'c'];
},
getOwnPropertyDescriptor(target, prop) {
// ๋ชจ๋ ํ๋กํผํฐ๋ฅผ ๋์์ผ๋ก ํ๋ฒ์ฉ ํธ์ถ๋จ
return {
enumerable: true,
configurable: true,
};
},
});
Object.keys(user); // ['a', 'b', 'c']
๐ก ๊ฐ์ฒด์ ํ๋กํผํฐ๊ฐ ์์ ๋ [[GetOwnProperty]]๋ฅผ ๊ฐ๋ก์ฑ์ ์ ์์์ฒ๋ผ ์ฌ์ฉํ ์ ์๋์ ๊ธฐ์ต.
ํ๋กํผํฐ ๋ณดํธ — deleteProperty
์ผ๋ฐ์ ์ผ๋ก ๋ด๋ถ์์๋ง ์ฌ์ฉํ๋ ํ๋กํผํฐ/๋ฉ์๋๋ ์ด๋ฆ ์์ _(์ธ๋์ค์ฝ์ด; ๋ฐ์ค)๋ฅผ ๋ถ์ด๋ ์ปจ๋ฒค์
์ด ์๋ค. ์์น์ ์ผ๋ก _ ๋ฐ์ค์ด ๋ถ์ผ๋ฉด ๊ฐ์ฒด ์ธ๋ถ์์ ์ด ํ๋กํผํฐ์ ์ ๊ทผํ ์ ์์ด์ผ ํ๋ค. ํ์ง๋ง ๋๋ถ๋ถ ์๋์ฒ๋ผ ์ ๊ทผํ ์ ์๋ค.
const user = {
name: 'Johan',
_password: '1q2w3e4r',
};
console.log(user._password); // 1q2w3e4r
Proxy๋ฅผ ์ด์ฉํด _๋ก ์์ํ๋ ๋ด๋ถ ํ๋กํผํฐ๋ฅผ ์ธ๋ถ์์ ์ ๊ทผํ์ง ๋ชปํ๋๋ก ํ ์ ์๋ค. ์ด ๊ธฐ๋ฅ์ ๊ตฌํํ๋ ค๋ฉด ์๋ ํธ๋ฉ์ด ํ์ํ๋ค.
| ํธ๋ฉ(๋ฉ์๋) | ์ฌ์ฉ ๋ชฉ์ |
| get | ํ๋กํผํฐ ์กฐํ ์ ์๋ฌ ์ถ๋ ฅ |
| set | ํ๋กํผํฐ ๊ฐ ๋ณ๊ฒฝ ์ ์๋ฌ ์ถ๋ ฅ |
| deleteProperty | ํ๋กํผํฐ ์ญ์ ์ ์๋ฌ ์ถ๋ ฅ |
| ownKeys | for...in ํน์ Object.keys ๊ฐ์ ํ๋กํผํฐ ์ํ ๋ฉ์๋ ์ฌ์ฉ ์ _๋ก ์์ํ๋ ํ๋กํผํฐ๋ ์ ์ธ |
// ์ฝ๋ ์ฐธ๊ณ JavaScript Info
let user = {
name: 'John',
checkPassword(value) {
// checkPassword(๋น๋ฐ๋ฒํธ ํ์ธ)๋ _password๋ฅผ ์ฝ์ ์ ์์ด์ผ ํ๋ค
return value === this._password;
},
_password: '***',
};
user = new Proxy(user, {
get(target, prop) {
// ํ๋กํผํฐ ์ฝ๊ธฐ๋ฅผ ๊ฐ๋ก์ฑ
if (prop.startsWith('_')) {
throw new Error('์ ๊ทผ์ด ์ ํ๋์ด์์ต๋๋ค.');
}
const value = target[prop];
return typeof value === 'function' ? value.bind(target) : value; // (*)
},
set(target, prop, value) {
// ํ๋กํผํฐ ์ฐ๊ธฐ๋ฅผ ๊ฐ๋ก์ฑ
if (prop.startsWith('_')) {
throw new Error('์ ๊ทผ์ด ์ ํ๋์ด์์ต๋๋ค.');
} else {
target[prop] = value;
return true;
}
},
deleteProperty(target, prop) {
// ํ๋กํผํฐ ์ญ์ ๋ฅผ ๊ฐ๋ก์ฑ
if (prop.startsWith('_')) {
throw new Error('์ ๊ทผ์ด ์ ํ๋์ด์์ต๋๋ค.');
} else {
delete target[prop];
return true;
}
},
ownKeys(target) {
// ํ๋กํผํฐ ์ํ๋ฅผ ๊ฐ๋ก์ฑ
return Object.keys(target).filter((key) => !key.startsWith('_'));
},
});
user._password; // [Get] Error
user._password = '1q2w3e4r'; // [Set] Error
delete user._password; // [Delete] Error
for (const key in user) console.log(key); // [OwnPropertyKeys] name
๐ก bind ๋ฉ์๋๋ ์ฒซ ๋ฒ์งธ ์ธ์์ ๋ช
์ํ this ์ปจํ
์คํธ๋ฅผ ๋ด์ ๋ฐ์ธ๋ฉ๋ ํจ์๋ฅผ ๋ฆฌํดํ๋ค.
get ํธ๋ฉ์์ ์๋์ฒ๋ผ value๊ฐ ํจ์์ผ ๋ value.bind(target) ๋ฐ์ธ๋ฉ๋ ํจ์๋ฅผ ๋ฆฌํดํ๊ณ ์๋ค. ๋น๋ฐ๋ฒํธ๋ฅผ ํ์ธํ๋ user.checkPassword() ๋ฉ์๋๊ฐ _password ๊ฐ์ ์ ๊ทผํ๋๋ก ํ๊ธฐ ์ํด์๋ค.
get(target, prop) {
// ...
let value = target[prop];
return (typeof value === 'function') ? value.bind(target) : value; // (*)
}
user.checkPassword()๋ฅผ ํธ์ถํ๋ฉด this๋ ํ๋ก์๋ก ๊ฐ์ผ user๊ฐ ๋๋๋ฐ(.์จ์ ์์ด user์ด๋ฏ๋ก), this._password๋ get ํธ๋ฉ์ ํ์ฑํํ๋ฏ๋ก ์๋ฌ๊ฐ ๋ฐ์ํ๋ค. ์ด๋ฐ ์ด์ ๋๋ฌธ์ ์๋ณธ ๊ฐ์ฒด(target) ์ปจํ
์คํธ๋ฅผ ๋ฐ์ธ๋ฉ์ํจ ๊ฒ. ๊ทธ๋ผ checkPassword()๋ฅผ ํธ์ถํ ๋ this๋ ํธ๋ฉ์ด ์๋ ์๋ณธ ๊ฐ์ฒด(target)๊ฐ ๋๊ณ , _password์ ์ ๊ทผํ ์ ์๊ฒ ๋๋ค.
์ ๋ฐฉ๋ฒ์ ๋๋ถ๋ถ ์ ์๋ํ์ง๋ง, ๋ฉ์๋๊ฐ Proxy๋ก ๊ฐ์ธ์ง ์์ ๊ฐ์ฒด๋ฅผ ๋๊ธฐ๋ ์๊ฐ ์๋ง์ง์ฐฝ์ด ๋ ์ ์๊ธฐ ๋๋ฌธ์ ์ด์์ ์ธ ๋ฐฉ๋ฒ์ ์๋๋ค. ๊ธฐ์กด ๊ฐ์ฒด์ Proxy๋ก ๊ฐ์ผ ๊ฐ์ฒด๊ฐ ์ด๋ ์๋์ง ํ์ ํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
๐ก ํด๋์ค์์ #์ ๋ถ์ด๋ฉด private ํ๋กํผํฐ/๋ฉ์๋๋ก ์ฌ์ฉํ ์ ์๋ค(private์ ์์ ๋ถ๊ฐ).
๋ฒ์ ํ์ธ — has
๐ก has ๋ฉ์๋(ํธ๋ฉ)๋ in ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ ๋ ์๋ํ๋ค — in ํธ์ถ์ ๊ฐ๋ก์ฑ๋ค.
has(target, property)
- target : ์๋ณธ ๊ฐ์ฒด.
new Proxy()์ฒซ๋ฒ์งธ ์ธ์์ ๋์ผ - property : ํ๋กํผํฐ ์ด๋ฆ
์๋๋ in ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํด ํน์ ์ซ์๊ฐ range ๋ด์ ์๋์ง ํ์ธํ๋ ์์. in ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ ๋ has ํธ๋ฉ์ด ์ด๋ฅผ ๊ฐ๋ก์ฑ๊ณ , ์
๋ ฅ๋ฐ์ prop์ด start์ end ํ๋กํผํฐ ์ฌ์ด์ ์๋ ์ซ์์ธ์ง ํ์ธํ๋ค.
let range = {
start: 1,
end: 10,
};
range = new Proxy(range, {
has(target, prop) {
return prop >= target.start && prop <= target.end;
},
});
console.log(5 in range); // true (์ด๋ prop์ 5)
console.log(11 in range); // false (์ด๋ prop์ 11)
๋ํผ ํจ์ — apply
๐ก apply ๋ฉ์๋(ํธ๋ฉ)์ ํจ์๋ฅผ ํธ์ถํ ๋ ์๋ํ๋ค.
apply(target, thisArg, args)
- target : ์๋ณธ ๊ฐ์ฒด (JS์์ ํจ์ ์ญ์ ๊ฐ์ฒด๋ค)
- thisArg :
this์ ๊ฐ - args : ํ๋ผ๋ฏธํฐ ๋ชฉ๋ก (arguments)
let func = function () {
return 'I am target';
};
func = new Proxy(func, {
apply(target, thisArg, args) {
return 'I am proxy';
},
});
func(); // 'I am proxy' (์ด๋ args๋ [])
func(2, 3); // ์ด๋ args [2, 3]
๋ฐ์ฝ๋ ์ดํฐ(Decorator)๋ ํ๋์ ์ฝ๋๋ฅผ ๋ค๋ฅธ ์ฝ๋๋ก ๋ํํ๊ฑฐ๋, ๋ค๋ฅธ ํจ์๋ฅผ ๋ํํ๋ ๋ฐฉ๋ฒ์ด๋ค. ์๋ ์ฝ๋์์ delay(f, ms)๋ฅผ ํธ์ถํ๋ฉด ํจ์๊ฐ ๋ฐํ๋๊ณ , ์ด ํจ์๋ ms ๋ฐ๋ฆฌ์ด ํ์ ์คํ๋๋ค.
function delay(f, ms) {
// ์ผ์ ์๊ฐ์ด ํ f๋ฅผ ํธ์ถํ๋ Wrapper ํจ์ ๋ฐํ
return function (...args) {
setTimeout(() => f.apply(this, args), ms);
};
}
let sayHi = function (user) {
console.log(`Hello, ${user}!`);
};
sayHi = delay(sayHi, 3000); // ƒ (...args) { setTimeout(...) }
sayHi('Johan'); // (3์ด ํ) Hello, Johan!
ํ์ง๋ง ๋ํผ ํจ์๋ ์ฝ๊ธฐ/์ฐ๊ธฐ ๋ฑ์ ์ฐ์ฐ์ ์ ๋ฌํ์ง ๋ชปํ๋ค. ๋ํผ ํจ์๋ก ํ ๋ฒ ๊ฐ์ธ๊ณ ๋์๋ถํด ๊ธฐ์กด ํจ์์ ์๋ name length ๊ฐ์ ํ๋กํผํฐ ์ ๋ณด๋ ์ฌ๋ผ์ง๋ค.
//...
console.log(sayHi.length); // 1 (ํจ์ ์ ์๋ถ์ ๋ช
์ํ ํ๋ผ๋ฏธํฐ์ ๊ฐฏ์)
sayHi = delay(sayHi, 3000);
console.log(sayHi.length); // 0 (๋ํผ ํจ์ ์ ์๋ถ์ ํ๋ผ๋ฏธํฐ๊ฐ ์์)
Proxy๋ฅผ ์ด์ฉํ๋ฉด ํ๊ฒ ๊ฐ์ฒด(์๋ณธ ํจ์)์ ๋ชจ๋ ๊ฑธ ์ ๋ฌํด์ฃผ๋ฏ๋ก ๋ ๊ฐ๋ ฅํ๋ค. ์๋ณธ ํจ์์ ์๋ name length ๊ฐ์ ํ๋กํผํฐ๋ ์ ๋๋ก ๋ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ค.
function delay(f, ms) {
return new Proxy(f, {
apply(target, thisArg, args) {
setTimeout(() => target.apply(thisArg, args), ms);
},
});
}
// let sayHi = function() {...} ์๋ต
sayHi = delay(sayHi, 3000);
console.log(sayHi.length); // 1 (ํ๋ฝ์์ ์ํ๋๋ ๋ชจ๋ ์ฐ์ฐ์ด ์๋ณธ ํจ์์ ์ ๋ฌ๋๋ค)
sayHi('Johan'); // (3์ด ํ) Hello, Johan!
Observable ๋ง๋ค๊ธฐ ์์
๐ก Symbol์ ๋ณ๊ฒฝ ๋ถ๊ฐ๋ฅํ ์์ ํ์
์ด๋ค. ํธ์ถํ ๋๋ง๋ค ๊ณ ์ ํ Symbol ๊ฐ์ ์์ฑํ๋ค. Symbol() ์ธ์์ ๋๊ธด ๋ฌธ์์ด์ Symbol ์์ฑ์ ์๋ฌด๋ฐ ์ํฅ๋ ์ฃผ์ง ์๋๋ค(๋๋ฒ๊น
์ฉ๋๋ก๋ง ์ฌ์ฉ).
Proxy๋ฅผ ์ด์ฉํด ์ ๋ฌ๋ฐ์ ๊ฐ์ฒด๋ฅผ ๊ด์ฐฐ ๊ฐ๋ฅํ๋๋ก(observable) ๋ง๋ค ์ ์๋ค. ์๋ ์์ ๋ ๊ฐ์ฒด ํ๋กํผํฐ ๊ฐ์ ๋ณ๊ฒฝํ ๋๋ง๋ค observe ๋ฉ์๋์ ๋๊ธด ์ฝ๋ฐฑ์ ์คํํด์ ํ๋กํผํฐ key, value(๋ณ๊ฒฝํ)๋ฅผ ์ถ๋ ฅํ๋ค.
user๊ฐ์ฒด์ observe ํจ์ ๋ฑ๋ก →obj[handlers]๋ฐฐ์ด์ ํจ์ ์ ์ฅuser.name๊ฐ ์ฐ๊ธฐ ์๋setํธ๋ฉ ์คํ ํnameํ๋กํผํฐ ๊ฐ ๋ณ๊ฒฝobj[handlers]๋ฐฐ์ด์ ์ ์ฅํ ์ฝ๋ฐฑ ์คํ
// ์ฝ๋ ์ฐธ๊ณ JavaScript Info
const handlers = Symbol('handlers');
function makeObservable(obj) {
obj[handlers] = []; // observe ๋ฉ์๋๊ฐ ๋ฐ์ handler ํจ์๋ฅผ ์ ์ฅํ ๋ฐฐ์ด
obj.observe = (handler) => {
// ์ ๋ฌ ๋ฐ์ handler ์ธ์ ํ์
์ด ํจ์์ผ๋๋ง obj[handlers] ๋ฐฐ์ด์ ์ ์ฅ
if (typeof handler === 'function') obj[handlers].push(handler);
};
return new Proxy(obj, {
// ํ๋กํผํฐ ๊ฐ ์ฝ๊ธฐ(get ํธ๋ฉ)๋ ์๋(set ํธ๋ฉ)์ ๋น์ทํ ๋ฐฉ๋ฒ์ผ๋ก ์์ฑํ ์ ์๋ค
set(target, prop, value, receiver) {
const success = Reflect.set(target, prop, value, receiver); // ํ๊ฒ ๊ฐ์ฒด์ ๋์ ์ ๋ฌ
if (success) target[handlers].forEach((handler) => handler(prop, value));
return success; // ํ๋กํผํฐ ์ฐ๊ธฐ ์ฑ๊ณต์ true, ์คํจ์ false
},
});
}
const user = makeObservable({});
user.observe((key, value) => console.log(`SET ${key} = ${value}`));
user.name = 'Johan'; // SET name = Johan
user.name = 'Smith'; // SET name = Smith
๐ก ์ต์๋ฒ ํจํด๊ณผ ๊ด๋ จํ ๋ ์์ธํ ๋ด์ฉ๊ณผ ๋ค๋ฅธ ์์ ๋ ๋งํฌ ์ฐธ๊ณ .
Reflect
๐ก ๋ฉํ ํ๋ก๊ทธ๋๋ฐ์ ํ๋ก๊ทธ๋จ ์คํ ์ค์ ์์ ํน์ ๋ค๋ฅธ ํ๋ก๊ทธ๋จ์ ์กฐ์ํ๋ ๊ธฐ๋ฒ์ ๊ฐ๋ฆฌํจ๋ค. ์์ ์ ์ฝ๋๋ฅผ ์ฝ๊ณ , ๋ณ๊ฒฝํ๊ฑฐ๋, ์๋ก์ด ์ฝ๋๋ฅผ ์์ฑํ๋ ๋ฑ์ ๋ค์ํ ๋์์ ํฌํจํ๋ค. ์๋ฐ์คํฌ๋ฆฝํธ์์ ๊ฐ์ฒด์ ๋์์ ๊ฐ๋ก์ฑ์ ์๋ก์ด ๊ธฐ๋ฅ์ ์ถ๊ฐํ ์ ์๋ Proxy์ ๊ฐ์ฒด์ ์์ฑ์ ์์ ํ๊ฒ ์ฝ๊ณ ์ฐ๋ Reflect ๋ฉ์๋๋ฅผ ํตํด์ ๋ฉํ ํ๋ก๊ทธ๋๋ฐ์ ์ง์ํ๋ค.
Reflect๋ ์๋ฐ์คํฌ๋ฆฝํธ์ ๋ฉํํ๋ก๊ทธ๋๋ฐ์ ์ง์ํ๋ ๋ด์ฅ ๊ฐ์ฒด๋ค. Reflect ๋ฉ์๋๋ Proxy์ ๊ฐ ํธ๋ฉ ์ธํฐํ์ด์ค์ ๋์ผํ๊ธฐ ๋๋ฌธ์(ํธ๋ฉ ๋ฐํ ์๊ตฌ์ฌํญ ์ถฉ์กฑ), Proxy ํธ๋ฉ ๋ด์์ ๊ธฐ๋ณธ ๋์์ ๋ ์์ฝ๊ฒ ๊ตฌํํ ์ ์๋ค.
Reflect.get(target, propertyKey[, receiver]) : Return target[property]
Reflect.set(target, propertyKey, value[, receiver]) : Return boolean
Reflect.has(target, propertyKey) : Return boolean
Reflect.deleteProperty(target, propertyKey) : Return boolean
Reflect.ownKeys(target) : Return array of property keys
…(๋ ๋ง์ ์ ์ ๋ฉ์๋๋ MDN ์ฐธ๊ณ )
์๋ฅผ๋ค์ด Set ํธ๋ฉ์์ target[prop] = value ์ ๊ฐ์ ์ฐ๊ธฐ ์์
ํ ์ผ์ผ์ด true ํน์ false๋ฅผ ๋ฐํํ๋ ๋์ , Reflect.set(target, prop, value) ํ ์ค๋ก ๊ฐ๋จํ๊ฒ ์ฒ๋ฆฌํ ์ ์๋ค.
const userProxy = new Proxy(
{ name: 'John', age: 30 },
{
set(target, prop, value) {
return Reflect.set(...arguments); // Reflect.set(target, prop, value)
// target[prop] = value;
// return true;
},
},
);
Reflect.get(...) ๋ฑ์ ๋ฉ์๋๋ ์ ํ์ ์ผ๋ก receiver ์ธ์๋ฅผ ๋ฐ๋๋ฐ, ์ด๋ฅผ ํตํด this ๋ฐ์ธ๋ฉ์ ์ค์ ํ ์ ์๋ค. ํนํ, ๊ฐ์ฒด์ ํ๋กํ ํ์
์ ๋ณ๊ฒฝํ ํ ๋ฐ์ํ ์ ์๋ ์ฌ์ด๋ ์ดํํธ ์ํฉ์์ ์ต์ด ์ ๊ทผ ์์ฒญ์ ๋ฐ์ ๊ฐ์ฒด์ this ๋ฐ์ธ๋ฉ์ ์ ์งํ ๋ ์ ์ฉํ๋ค.
Receiver ๊ฐ๋
๐ก target[property]๊ฐ getter/setter(์ ๊ทผ์ ํ๋กํผํฐ)์ด๋ฉด Receiver๊ฐ this ์ปจํ
์คํธ๋ก ๋์ํ๋ค.
JS์์ ํ๋กํ ํ์
์ฒด์ด๋์ด ์ด๋ค์ง๋๋ผ๋ ์ต์ด ์ ๊ทผ ์์ฒญ์ ๋ฐ์ ๊ฐ์ฒด๋ฅผ ์ ์งํ๋ค. ์ด ๊ฐ์ฒด๋ฅผ Receiver๋ผ๊ณ ๋ถ๋ฅธ๋ค. target[property]๊ฐ getter/setter๋ผ๋ฉด Receiver๊ฐ this ์ปจํ
์คํธ๋ก ๋์ํ๋ค.
์๋ ์์ ์์ child.age ํ๋กํผํฐ ์ฝ๊ธฐ๋ฅผ ์๋ํ๋ฉด [[Get]] ๋ด๋ถ ์ฌ๋กฏ ๋ฉ์๋๋ฅผ ํธ์ถํ๊ณ ์ด๋ Receiver๋ child ๊ฐ์ฒด๊ฐ ๋๋ค. — child.[[Get]](P, Receiver)
child ๊ฐ์ฒด์ age๊ฐ ์์ผ๋ฏ๋ก ํ๋กํ ํ์
์ฒด์ด๋์ ํตํด parent ๊ฐ์ฒด์ age getter*๋ฅผ ์คํํ๋ค. ์ด๋ Receiver(child)๊ฐ this๋ก ์ฌ์ฉ๋ผ์ this._birthYear๋ 1990์ด ๋๋ค. *setter ์ญ์ ๋น์ทํ ๊ณผ์ ์ ๊ฑฐ์ณ Receiver(child)๊ฐ this๋ก ์ฌ์ฉ๋๋ค.
// ์ฝ๋ ์ฐธ๊ณ Toast UI
const child = {
_birthYear: 1990,
};
const parent = {
_birthYear: 1984,
get age() {
return new Date().getFullYear() - this._birthYear;
},
set birthYear(year) {
this._birthYear = year;
},
};
Object.setPrototypeOf(child, parent); // child.__proto__ = parent;
console.log(child.age); // 32 (2022 - 1990)
Reflect ํ์ฉ
this ๋ฐ์ธ๋ฉ
Reflect์ receiver ํ๋ผ๋ฏธํฐ๋ getter/setter(์ ๊ทผ์ ํ๋กํผํฐ)์์ this๋ก ์ฌ์ฉ๋๋ค.
Reflect ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ฉด receiver ํ๋ผ๋ฏธํฐ๋ฅผ ํตํด this ๋ฐ์ธ๋ฉ์ ์กฐ์ํ ์ ์๋ค. target๊ณผ receiver๊ฐ ๋ค๋ฅด๊ณ , target[property]๊ฐ getter/setter์ด๋ฉด receiver ํ๋กํผํฐ๋ฅผ ์ฝ๊ฑฐ๋ ์ด๋ค. ์ฆ, target์ getter/setter์์ this๋ receiver๊ฐ ๋๋ค.
const target = {
one: 10,
two: 20,
get sum() {
return this.one + this.two;
},
};
const receiver = {
one: 30,
two: 40,
get sum() {
return this.one + this.two + 999;
},
};
// target์ ๋ฐ์ดํฐ ํ๋กํผํฐ์ ์ ๊ทผํ ๋ (target.one)
// target.one์ ๋ฐ์ดํฐ ํ๋กํผํฐ์ด๋ฏ๋ก receiver ํ๋ผ๋ฏธํฐ๋ ์๋ฌด ์ญํ ๋ ํ์ง ์๋๋ค
Reflect.get(target, 'one', target); // 10
Reflect.get(target, 'one', receiver); // 10
// target์ ์ ๊ทผ์ ํ๋กํผํฐ(getter/setter)์ ์ ๊ทผํ ๋ (target.sum)
Reflect.get(target, 'sum', target); // 30 (sum getters this: target)
Reflect.get(target, 'sum', receiver); // 70 (sum getters this: receiver)
ํ๋กํ ํ์ ์ฌ์ด๋ ์ดํฉํธ ํด๊ฒฐ
์๋ ์์ ์์ admin.name ๊ฐ์ ์กฐํํ๋ฉด Admin์ด ์๋ Guest๋ฅผ ๋ฐํํ๋ค.
admin ๊ฐ์ฒด์ name ํ๋กํผํฐ๊ฐ ์์ผ๋ฏ๋ก ํ๋กํ ํ์
์ฒด์ด๋์ ํตํด(via __proto__) userProxy ๊ฐ์ฒด์์ name์ ์ฐพ๋๋ค. ๊ทธ๋ผ get ํธ๋ฉ์ด ํธ๋ฆฌ๊ฑฐ๋๊ณ target[prop] ๊ฐ์ ๋ฐํํ๋๋ฐ, target[prop]์ด getter๋ผ๋ฉด target ์ปจํ
์คํธ์์ ์คํ๋๋ค. ๋๋ฌธ์ name getter ์์์ this๋ user(target)๊ฐ ๋๋ค.
// ํ๋กํ ํ์
์ฌ์ด๋ ์ดํํธ ์์ via JavaScript Info
const user = {
_name: 'Guest',
get name() {
return this._name;
},
};
const userProxy = new Proxy(user, {
get(target, prop, receiver) {
return target[prop]; // target === user
},
});
const admin = {
__proto__: userProxy,
_name: 'Admin',
};
console.log(admin.name); // Guest
์ ๊ฐ์ ์ํฉ์์ Reflect๋ฅผ ์ฌ์ฉํ๋ฉด getter์ ์ฌ๋ฐ๋ฅธ ์ปจํ
์คํธ๋ฅผ ์ ๋ฌํ ์ ์๋ค. receiver๋ ์ต์ด ์ ๊ทผ ์์ฒญ์ ๋ฐ์ ๊ฐ์ฒด(admin)๋ฅผ ์ ์งํ๋ฏ๋ก ์ด๋ฅผ Reflect.get์ ๋๊ธฐ๋ฉด getter๋ admin ์ปจํ
์คํธ์์ ์คํ๋๋ค. ์ฆ, name getter์์ this๋ admin์ด ๋๋ค.
const userProxy = new Proxy(user, {
get(target, prop, receiver) {
// return target[prop];
return Reflect.get(target, prop, receiver);
},
});
์ค์ฒฉ ํ๋ก์
์๋์ฒ๋ผ ์ค์ฒฉ ๊ฐ์ฒด ํ์์ผ๋ก ํ ๋น์ ์๋ํ๋ฉด 1๋์ค์ ์๋ wrapper ๊ฐ์ฒด๋ Proxy๋ฅผ ํตํด ๊ด๋ฆฌ๋์ง๋ง, ๊ทธ ๋ด๋ถ์ ์๋ ๊ฐ์ฒด(์๋ ์์์์ a ์์ฑ)๋ ๋ฐ๋ก Proxy๋ก ๊ด๋ฆฌ๋์ง ์๋๋ค.
const origin = {};
const handler = {
get(target, prop, receiver) {
console.log('Get ํธ๋ฉ ์คํ');
return Reflect.get(...arguments);
},
set(target, prop, value) {
console.log('Set ํธ๋ฉ ์คํ');
return Reflect.set(...arguments);
},
};
const wrapper = new Proxy(origin, handler);
wrapper.a = {};
wrapper.a.b = 20;
console.log(wrapper);
// Set ํธ๋ฉ ์คํ
// Get ํธ๋ฉ ์คํ
// Proxy(Object) {a: {b: 20}} -> wrapper๋ Proxy ๊ฐ์ฒด, wrapper.a๋ ์ผ๋ฐ ๊ฐ์ฒด
wrapper.a = {}: Set ํธ๋ฉ์ด ์คํ๋ผ์a์์ฑ์{}๋น ๊ฐ์ฒด๋ฅผ ํ ๋นํ๋ค.wrapper.a.b = 20:- ๋จผ์
wrapper.a์์ฑ์ ์ ๊ทผํ๊ธฐ ์ํด Get ํธ๋ฉ์ด ์คํ๋๊ณ{}๋น ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ค. - ๋ฐํ๋ ๊ฐ์ฒด์
b์์ฑ์20์ผ๋ก ์ง์ ํ ๋นํ๊ธฐ ๋๋ฌธ์ Set ํธ๋ฉ์ ์คํ๋์ง ์๋๋ค.
- ๋จผ์
๋ง์ฝ ๋ด๋ถ ๊ฐ์ฒด๋ ๋ชจ๋ Proxy๋ก ๊ด๋ฆฌํ๊ณ ์ถ๋ค๋ฉด Get ํธ๋ฉ์์ ๋ฐํํ๋ ๊ฐ์ด ๊ฐ์ฒด ํ์ ์ผ ๋ ํด๋น ๊ฐ์ฒด์๋ ๋์ ์ผ๋ก Proxy๋ฅผ ์ ์ฉํ๋ ๋ฐฉ์์ผ๋ก ๊ตฌํํ๋ฉด ๋๋ค. ์กฐํํ๋ ๊ฐ์ด ๊ฐ์ฒด๋ผ๋ฉด ํญ์ ์๋ก์ด Proxy๋ฅผ ์์ฑํ๊ธฐ ๋๋ฌธ์ ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง๋ฅผ ์ํด ์ด๋ฏธ ์์ฑํ Proxy๋ ์บ์๋ก ๊ด๋ฆฌํด์ ์ฌ์ฌ์ฉํ๋ ๋ก์ง๋ ํ์ํ๋ค.
const origin = {};
// WeakMap์ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ๋ ๊ณณ์ด ์๋ค๋ฉด ๊ฐ๋น์ง ์ปฌ๋ ์
๋์์ด ๋ผ์ ๋ฉ๋ชจ๋ฆฌ์ WeakMap์์ ์ญ์ ๋๋ค
const proxyCache = new WeakMap();
const handler = {
get(target, prop, receiver) {
console.log('Get ํธ๋ฉ ์คํ');
const value = Reflect.get(...arguments);
if (typeof value === 'object' && value !== null) {
if (proxyCache.has(value)) {
return proxyCache.get(value); // ์ ์ฅ๋ Proxy ์บ์๊ฐ ์๋ค๋ฉด ๋ฐํ
} else {
const newProxy = new Proxy(value, handler); // ์ ์ฅ๋ Proxy ์บ์๊ฐ ์์ด์ ์๋ก์ด Proxy ๊ฐ์ฒด ์์ฑ
proxyCache.set(value, newProxy); // ์๋ก ์์ฑํ Proxy ๊ฐ์ฒด๋ฅผ ์บ์์ ์ ์ฅ
return newProxy;
}
}
return value;
},
set(target, prop, value) {
console.log('Set ํธ๋ฉ ์คํ');
return Reflect.set(...arguments);
},
};
const wrapper = new Proxy(origin, handler);
wrapper.a = {};
wrapper.a.b = 20;
console.log(wrapper);
// Set ํธ๋ฉ ์คํ
// Get ํธ๋ฉ ์คํ
// Set ํธ๋ฉ ์คํ
// Proxy(Object) {a: {b: 20}} -> wrapper, wrapper.a ๋ชจ๋ Proxy ๊ฐ์ฒด
wrapper.a: Set ํธ๋ฉ์ด ์คํ๋ผ์a์์ฑ์{}๋น ๊ฐ์ฒด๋ฅผ ํ ๋นํ๋ค.wrapper.a.b = 20:- Get ํธ๋ฉ์ด ์คํ๋ผ์
a์์ฑ ๊ฐ์ด ๊ฐ์ฒด์ธ์ง ํ์ธํ๋ค.a์์ฑ ๊ฐ์ ๋น ๊ฐ์ฒด์ด๊ณproxyCache์ ์กด์ฌํ์ง ์๊ธฐ ๋๋ฌธ์ ์๋ก์ด Proxy ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณproxyCache์ ์ ์ฅํ ํ ๋ฐํํ๋ค. - ๋ฐํ๋ Proxy ๊ฐ์ฒด์
b์์ฑ์ ์ ๊ทผํ๊ธฐ ๋๋ฌธ์ Set ํธ๋ฉ์ด ์คํ๋๊ณ ,b์์ฑ์20์ ํ ๋นํ๋ค.
- Get ํธ๋ฉ์ด ์คํ๋ผ์
๋ ํผ๋ฐ์ค
'๐ช Programming' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๋๊ธ
์ด ๊ธ ๊ณต์ ํ๊ธฐ
-
๊ตฌ๋
ํ๊ธฐ
๊ตฌ๋ ํ๊ธฐ
-
์นด์นด์คํก
์นด์นด์คํก
-
๋ผ์ธ
๋ผ์ธ
-
ํธ์ํฐ
ํธ์ํฐ
-
Facebook
Facebook
-
์นด์นด์ค์คํ ๋ฆฌ
์นด์นด์ค์คํ ๋ฆฌ
-
๋ฐด๋
๋ฐด๋
-
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
-
Pocket
Pocket
-
Evernote
Evernote
๋ค๋ฅธ ๊ธ
-
[React] ๋ฆฌ์กํธ๋ก ์ค์๊ฐ ๊ฒ์์ฐฝ ๊ตฌํํ๊ธฐ
[React] ๋ฆฌ์กํธ๋ก ์ค์๊ฐ ๊ฒ์์ฐฝ ๊ตฌํํ๊ธฐ
2024.04.30 -
[HTML/CSS] ํผ ํ๋(input) ์์ ๋ณ๊ฒฝํ๊ธฐ — accent-color
[HTML/CSS] ํผ ํ๋(input) ์์ ๋ณ๊ฒฝํ๊ธฐ — accent-color
2024.04.29 -
[JS] ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ์ฒด ํ๋กํผํฐ ์ค๋ช ์ / ํ๋๊ทธ / ์ต์๋ฒ ํจํด
[JS] ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ์ฒด ํ๋กํผํฐ ์ค๋ช ์ / ํ๋๊ทธ / ์ต์๋ฒ ํจํด
2024.04.29 -
[JS] ์๋ฐ์คํฌ๋ฆฝํธ ๋๋ฐ์ด์ค Debounce, ์ค๋กํ Throttle ๊ตฌํํ๊ธฐ
[JS] ์๋ฐ์คํฌ๋ฆฝํธ ๋๋ฐ์ด์ค Debounce, ์ค๋กํ Throttle ๊ตฌํํ๊ธฐ
2024.04.29