๐งฉ Javascript์ Object
์ต๊ทผ ๊น์ ๋ณต์ฌ๋ฅผ ์ํ ์ ํธ๋ฆฌํฐ ํจ์๋ฅผ ์ง์ ๊ตฌํํ๋ผ๋ ๊ณผ์ ๋ฅผ ๋ฐ์๋ค.
๋จ์ํ ๊ฐ์ฒด๋ง์ด ์๋๋ผ Array, Map, Set์ ๋ฌผ๋ก ์ปค์คํ ๊ฐ์ฒด๊น์ง ๋ชจ๋ ์ ๋๋ก ๋ณต์ฌํ ์ ์์ด์ผ ํ๋ค. ์ฒ์์ ํค-๊ฐ ์์ ์ฌ๊ท์ ์ผ๋ก ์ ๊ฐ์ฒด์ ๋ณต์ฌํ๋ ๋ฐฉ์์ผ๋ก ํด๊ฒฐํ๋ ค ํ์ง๋ง, ๊ณง ๋ค์ํ ๊ฐ์ฒด ํ์ ์ ์ ํํ ๋ค๋ฃจ๋ ค๋ฉด ์๋ฐ์คํฌ๋ฆฝํธ์ Object, ์์ฑ ๊ตฌ์กฐ, ๊ทธ๋ฆฌ๊ณ ํ๋กํ ํ์ ์ฒด๊ณ์ ๋ํ ์ดํด๊ฐ ํ์์ ์ด์๋ค.
์ด ๊ณผ์ ์์ ๋ด๊ฐ ๊ฐ์ฒด ์์คํ ์ ๋ํด ์ผ๋ง๋ ํผ์์ ์ผ๋ก ์๊ณ ์์๋์ง ์ค๊ฐํ๊ณ , ์ด๋ฅผ ๊ณ๊ธฐ๋ก ๊ด๋ จ ๊ฐ๋ ์ ๊น์ด ์๊ฒ ํ์ตํ๊ณ ์ ๋ฆฌํด๋ณด๊ฒ ๋์๋ค.
1) Object๋?
๊ฐ์ฒด๋ **๋ฐ์ดํฐ์ ๊ธฐ๋ฅ(๋ฉ์๋)**๋ฅผ ํคโ๊ฐ ์์ผ๋ก ์ ์ฅํ๋ ๊ตฌ์กฐ.
const person = { name: 'MyName', age: 25, greeting() { console.log("Hello, my name is", this.name); }};name,age๋ ์์ฑ(property),greeting์ ๋ฉ์๋(method).
2) ๊ฐ์ฒด ์์ฑ ๋ฐฉ๋ฒ
โ ๋ฆฌํฐ๋ด
const obj = { key: 'value', number: 12345 };โ ์์ฑ์ & Object.create
const obj = new Object();obj.key = 'value';obj.number = 12345;
// ์๋ณธ์ ํ๋กํ ํ์
/๋์คํฌ๋ฆฝํฐ๊น์ง ์ ์ง ๋ณต์ฌconst clone = Object.create( Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));Object.create(proto, descriptors): ์ง์ ํ ํ๋กํ ํ์ ๊ณผ ๋์คํฌ๋ฆฝํฐ๋ก ์ ๊ฐ์ฒด ์์ฑ.
โ ํด๋์ค
class Person { constructor(name, age) { this.name = name; this.age = age; }}const personA = new Person("a", 27);โ Getter/Setter
const person = { firstName: 'value', lastName: '12345', get fullName() { return `${this.firstName} ${this.lastName}`; }, set fullName(name) { [this.firstName, this.lastName] = name.split(" "); }};3) ์์ฑ ์ ๊ทผ
const a = new Person("a", 27);console.log(a.name); // ์ ํ๊ธฐ๋ฒconsole.log(a['name']); // ๋๊ดํธ ํ๊ธฐ๋ฒ4) ์์ฑ ์ถ๊ฐ/์์ /์ญ์
const p = new Person("a", 27);p.job = 'Developer'; // ๋ฑ๋กp.age = 26; // ๋ณ๊ฒฝdelete p.job; // ์ญ์ Object.defineProperty / defineProperties
const p2 = new Person("a", 27);Object.defineProperties(p2, { job: { value: 'Developer', writable: true, configurable: true, enumerable: true }});Object.defineProperty(p2, "favoriteFood", { value: 'apple', writable: true, configurable: true, enumerable: true});- ๋์คํฌ๋ฆฝํฐ๋ก ์ฐ๊ธฐ/์ด๊ฑฐ/์ฌ์ ์ ๊ฐ๋ฅ ์ฌ๋ถ๋ฅผ ์ ์ด.
- writable
- configurable
- enumerable
5) ๋ณ๊ฒฝ ์ ์ฝ: freeze / seal (๏ผpreventExtensions)
Object.freeze()๋ฉ์๋๋ฅผ ํตํด ๊ฐ์ฒด๋ฅผ ๋๊ฒฐํ๊ฒ ๋๋ฉด ๋๊ฒฐ๋ ๊ฐ์ฒด๋ ๋ ์ด์ ์๋ก์ด ์์ฑ์ ์ถ๊ฐํ๊ฑฐ๋ ์ ๊ฑฐํ๋ ๊ฒ์ ๋ฐฉ์งํ๋ค.- ์กด์ฌํ๋ ์์ฑ์ ๋ถ๋ณ์ฑ, ์ค์ ๊ฐ๋ฅ์ฑ, ์์ฑ ๊ฐ๋ฅ์ฑ์ด ๋ณ๊ฒฝ๋๋ ๊ฒ ์ญ์ ๋ฐฉ์ง๋๋ฉฐ, ์กด์ฌ ์์ฑ์ ๊ฐ์ ์์ ํ๋ ๊ฒ๋ ๋ฐฉ์งํ๋ค.
Object.seal()๋ฉ์๋๋ฅผ ํตํด ๊ฐ์ฒด๋ฅผ ๋ฐ๋ดํ๊ฒ ๋๋ฉด ์๋ก์ด ์์ฑ์ ์ถ๊ฐ๊ฐ ๋ถ๊ฐ๋ฅํ๋ฉฐ ํ์ฌ ์กด์ฌํ๋ ์์ฑ์ ์ค์ ๋ถ๊ฐ๋ฅ ์ํ๋ก ๋ง๋ค์ด์ค๋ค. ๋ค๋ง ์ฐ๊ธฐ ๊ฐ๋ฅํ ์์ฑ์ ๋ํ ์์ฑ์ ๊ฐ์ ๋ฐ๋ด ํ์๋ ๋ณ๊ฒฝํ ์ ์๋ค๋ ์ ์์Object.freeze()๋ฉ์๋์ ์ฐจ์ด๊ฐ ์๋ค.- ๋๊ฒฐ/๋ฐ๋ด ์ฌ๋ถ๋
Object.isFrozen(),Object.isSealed()๋ฉ์๋๋ฅผ ํตํด ํ์ธํ ์ ์๋ค.
const f = Object.freeze(new Person("a", 25));f.age = 27; // ๋ฌด์console.log(Object.isFrozen(f), Object.isSealed(f)); // true, true
const s = Object.seal(new Person("a", 26));s.age = 27; // ๊ฐ๋ฅ(์์ฑ writable์ด๋ฉด)console.log(Object.isFrozen(s), Object.isSealed(s)); // false, true| ๋ฉ์๋ | ์ ์์ฑ ์ถ๊ฐ | ๊ธฐ์กด ์์ฑ ์ญ์ | ๊ฐ ๋ณ๊ฒฝ(์ฐ๊ธฐ) | ๋์คํฌ๋ฆฝํฐ ๋ณ๊ฒฝ |
|---|---|---|---|---|
Object.freeze | โ | โ | โ | โ |
Object.seal | โ | โ | โ (writable์ผ ๋) | ์ผ๋ถ โ |
Object.preventExtensions | โ | โ | โ | โ |
6) ์์ฑ ๋ณด์ ํ์ธ: hasOwnProperty vs Object.hasOwn
๊ฐ์ฒด๊ฐ ํน์ ์์ฑ์ ์์ ์ ์์ฑ์ผ๋ก ๊ฐ์ง๊ณ ์๋์ง ํ์ธํ ๋ hasOwnproperty ๋ฉ์๋๋ ES2022์ ์๋ก ์ถ๊ฐ๋ ์ ์ ๋ฉ์๋์ธ Object.hasOwn()์ ์ฌ์ฉํ ์ ์๋ค.
class Person { constructor(name, age) { this.name = name; this.age = age; } greeting() { console.log("Hi", this.name); }}const p = new Person("person", 25);p.sayHello = function() { console.log("Hello", this.name); };
console.log(p.hasOwnProperty("name")); // trueconsole.log(Object.hasOwn(p, "name")); // trueconsole.log(Object.hasOwn(p, "sayHello")); // trueconsole.log(Object.hasOwn(p, "greeting")); // false (ํ๋กํ ํ์
์ ๋ฉ์๋)Object.hasOwn(obj, key)๋ ์์ ํ ์ ์ ๋ฉ์๋ (์ธ์คํด์ค๊ฐhasOwnProperty๋ฅผ ์ค๋ฒ๋ผ์ด๋ํด๋ ์์ ).
๐งฌ ํ๋กํ ํ์ ์ฒด์ธ
๐ ํ๋กํ ํ์
- ์๋ฐ์คํฌ๋ฆฝํธ์ ๋ชจ๋ ๊ฐ์ฒด๋ ๋ด๋ถ
[[Prototype]]๋งํฌ๋ฅผ ๊ฐ์ง๋ค. - ์ด๋ฌํ ๊ตฌ์กฐ๋ฅผ ํตํด ๊ฐ์ฒด๋ ํด๋์ค ๊ธฐ๋ฐ ์ธ์ด์ ์์์ฒ๋ผ ๊ณตํต ์์ฑ๊ณผ ๋ฉ์๋๋ฅผ ์์๋ฐ๊ฑฐ๋ ์ฐธ์กฐํ ์ ์์ผ๋ฉฐ, ์ด๋ ๊ฒ ํ์ฑ๋ ์์ ๊ตฌ์กฐ๋ฅผ **ํ๋กํ ํ์
์ฒด์ธ(Prototype Chain)**์ด๋ผ๊ณ ํ๋ค.
- ์์ผ๋ฉด ์๊ธฐ ์์ โ ํ๋กํ ํ์ โ โฆ โ Object.prototype โ null๋ก ํ์.
const arr = [1, 2, 3];console.log(arr.toString()); // "1,2,3" (Array.prototype โ Object.prototype)๐ ๏ธ ์์ฑ์ ํจ์์ ํ๋กํ ํ์
-
new ์ฐ์ฐ์๋ก ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ฉด ํด๋น ๊ฐ์ฒด๋ ์์ฑ์ ํจ์์ prototype ์์ฑ์ ์์ ์ [[Prototype]]์ผ๋ก ์ฐ๊ฒฐํ๋ค.
function Person(name) {this.name = name;}Person.prototype.sayHello = function () { console.log(`Hi, I'm ${this.name}`); };const p1 = new Person("Alice");console.log(p1.sayHello === Person.prototype.sayHello); // true
7) ์์ฑ ์ค๋ช ์ ์กฐํ
const p = new Person("a", 1);Object.defineProperty(p, "job", { value: 'Developer', writable: true, configurable: false, enumerable: true});console.log(Object.getOwnPropertyDescriptors(p));getOwnPropertyDescriptors๋ก ๋ชจ๋ own ์์ฑ์ ๋์คํฌ๋ฆฝํฐ๋ฅผ ์กฐํํ ์ ์๋ค.
8) ๊ฐ์ฒด ์ํ
forโฆin
// for...in (์์ + ์์๋ enumerable ํฌํจ)for (const k in p) console.log(k, p[k]);Object.keys / values / entries
// own enumerable๋งObject.keys(p); // ํค ๋ฐฐ์ดObject.values(p); // ๊ฐ ๋ฐฐ์ดObject.entries(p); // [ํค,๊ฐ] ๋ฐฐ์ด (์ ์ ํค โ ๋ฌธ์์ด ํค ์ฝ์
์์)์ด๊ฑฐ ๋ถ๊ฐ๋ฅ/์ฌ๋ณผ ํค
const obj = { a: 1, b: 2 };Object.defineProperty(obj, "c", { value: 3, enumerable: false });
console.log(Object.keys(obj)); // ['a','b']console.log(Object.getOwnPropertyNames(obj));// ['a','b','c']
const sym = Symbol('id');const o2 = { [sym]: 123, a: 1 };console.log(Object.getOwnPropertySymbols(o2)); // [Symbol(id)]9) Map โ Object ๋ณํ
Object.entries()๋ forโฆin๊ณผ ๊ฐ์ ์์๋ก ์ฃผ์ด์ง ๊ฐ์ฒด ์์ฒด์ enumerable ์์ฑ์ ํค-๊ฐ ์ ํํ์ ๋ฐฐ์ด๋ก ๋ฐํํ๋ค. ์ด๋ฅผ ํ์ฉํ์ฌ Map์ผ๋ก ๋ณํํ ์ ์๋ค.- ๋ฐ๋๋ก
Object.fromEntries๋ ํค-๊ฐ ์ ํํ์ ๋ฐฐ์ด์ ๊ฐ์ฒด๋ก ๋ณํํด์ฃผ๋ ๋ฉ์๋์ด๋ค. ์ด๋ฅผ ํตํด Map์ Object๋ก ๋ณํํ ์ ์๋ค.
const object = { a: 1, b: 2, c: 3 };const map = new Map(Object.entries(object)); // Object โ Map
const fromMap = new Map([["a",1],["b",2]]);const toObj = Object.fromEntries(fromMap); // Map โ Object10) ๊ฐ์ฒด ๋ณต์ฌ
โ ์์ ๋ณต์ฌ
const copy1 = Object.assign({}, person);const copy2 = { ...person };
// ํ๋กํ ํ์
/๋น์ด๊ฑฐ ์์ฑ๊น์ง ์ด๋ฆฌ๋ ์์ ๋ณต์ฌconst copyAll = Object.create( Object.getPrototypeOf(person), Object.getOwnPropertyDescriptors(person));- ์์ ๋ณต์ฌ ์์๋ ์ค์ฒฉ ๊ฐ์ฒด๋ ์ฐธ์กฐ ๊ณต์ โ ๋ด๋ถ ๋ณ๊ฒฝ์ด ์๋ณธ์ ์ ํํ๋ค.
Object.assign()์ ํตํ ๋ณต์ฌ๋ ๋ชฉํ ๊ฐ์ฒด๋ก ์ด๊ฑฐ ๊ฐ๋ฅํ ์์ฑ๊ณผ ๊ฐ์ฒด์ ์์ฑ๋ค๋ง ๋ณต์ฌํ ์ ์์ง๋งObject.getPrototypeOf,Object.getOwnPropertyDescriptors๋ฅผ ํ์ฉํ๋ฉด ์ด๊ฑฐ ๋ถ๊ฐ๋ฅํ ์์ฑ๋ค๋ ๋ณต์ฌํ ์ ์๋ค.
โ ๋ณํฉ
const m = Object.assign({}, obj1, obj2); // ๋ค์ ์จ ํค๊ฐ ๋ฎ์ด์const m2 = { ...obj1, ...obj2 };- Spread ์ฐ์ฐ์, Object.assign ๋ฉ์๋๋ฅผ ํ์ฉํด ๋ค์ค ๊ฐ์ฒด๋ฅผ ๋ณํฉํ ์ ์๋ค. ๋ค๋ง ํ์์ ํค๊ฐ ๋ฎ์ด์์์ง๊ธฐ ๋๋ฌธ์ ๋ณํฉ ์์์ ์ ์ํด์ผ ํ๋ค.
โ ๊น์ ๋ณต์ฌ (์ฃผ์: JSON ๊ธฐ๋ฐ ํ๊ณ)
const deepCopied = JSON.parse(JSON.stringify(person));- JSON.parse, JSON.stringify๋ฅผ ํ์ฉํ์ฌ ๊ฐ๋จํ๊ฒ ๊น์ ๋ณต์ฌ๋ฅผ ํ ์ ์๋ค. ๋ค๋ง JSON.parse, JSON.stringify๋ฅผ ์ฌ์ฉํ ๊น์ ๋ณต์ฌ์๋ ์ฌ๋ฌ๊ฐ์ง ์ ์ฝ์ฌํญ ๋ฐ ํ๊ณ๊ฐ ์กด์ฌํ๋ค.
- ๋ฌด์/๋ถ๊ฐ:
undefined,Function,Symbol,BigInt(์๋ฌ),Map/Set,Date/RegExp(์์ค), ์ํ ์ฐธ์กฐ(์๋ฌ). - ๋์: ์ง์ ๊ตฌํ ๋๋
lodash.cloneDeep.
- ๋ฌด์/๋ถ๊ฐ:
11) ๊ฐ์ฒด ๋น๊ต
๊ฐ์ฒด๋ ์ฐธ์กฐํ ํ์ ์ผ๋ก ๋ด๋ถ ์์ฑ์ ๊ฐ์ด ๋์ผํ๋๋ผ๋ ์ฐธ์กฐ๊ฐ์ด ๋ค๋ฅผ ๊ฒฝ์ฐ ์๋ก ๋ค๋ฅธ ๊ฐ์ฒด๋ก ๊ฐ์ฃผ๋๋ค.
const person = new Person("a", 26);const copy = { ...person };console.log(person === copy); // false (์ฐธ์กฐ ๋น๊ต)โ๊ฐ ๋๋ฑโ ๊ฒ์ฌ(๊ฐ๋จ ์ฉ๋)
JSON.stringify(person) === JSON.stringify(copy);- ๊ฐ์ฒด ๋ด๋ถ ์์ฑ ๊ฐ์ด ์ค์ ๋ก ๋์ผํ์ง ๋น๊ตํ ๋, JSON.stringify๋ฅผ ํ์ฉํ ์ ์๋ค. ๋จ, JSON.stringify๋ ํจ์, undefined, Symbol ๋ฑ์ ๋ฌด์ํ๊ณ , ์์ฑ ์์๊ฐ ๋ค๋ฅผ ๊ฒฝ์ฐ ๋ค๋ฅธ ๊ฒฝ์ฐ๊ฐ ๋์ฌ ์ ์๋ค.
- ํจ์/undefined/Symbol ๋ฌด์, ํค ์์ ์ํฅ ๊ฐ๋ฅ.
- ์ ๊ตํ ๋น๊ต๋
lodash.isEqual๋ฑ ์ฌ์ฉ ๊ถ์ฅ.