Photo by Greg Rakozy on Unsplash

JavaScript Object ์— ๋Œ€ํ•œ ์ „๋ฐ˜์ ์ธ ๋‚ด์šฉ์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค

์ด ๊ธ€์€ ์•„๋ž˜ ์ฑ…๊ณผ ๋‚ด์šฉ์„ ์ฐธ๊ณ  ํ•˜์˜€์Šต๋‹ˆ๋‹ค

  1. You Don't Know JS - this & Object Prototypes
  2. ์ธ์‚ฌ์ด๋“œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ
  3. MDN / ๊ฐ์ฒด์ง€ํ–ฅ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ
  4. MDN / Javascript Object Basic
  5. MDN / Working with Objects

(์ด ๊ธ€์€ Notion ์•ฑ ์œผ๋กœ ์ž‘์„ฑํ•œ ์›น ํŽ˜์ด์ง€์—์„œ๋„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค! ๋‚ด์šฉ์€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค)

๐Ÿ“š Contents


  • ๊ฐ์ฒด์˜ ์ƒ์„ฑ (์ดˆ๊ธฐํ™”)
  • ๋‚ด์žฅ ๊ฐ์ฒด (Built-in objects)
  • Property
    • Property Name
    • Property Descriptor
      • Data Descriptor
      • Accessor Descriptor
    • Reference & Immutability
      • Reference
      • Immutability
    • Prototype Chaining

๐Ÿฃ ๊ฐ์ฒด์˜ ์ƒ์„ฑ (์ดˆ๊ธฐํ™”)


๊ฐ์ฒด์˜ ์ƒ์„ฑํ•˜๋Š” ์„ธ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค

  • new Object()
  • Object.create()
  • {}

์šฐ๋ฆฌ๊ฐ€ ํ”ํžˆ ์‚ฌ์šฉํ•˜๋Š” {} ๋Š” Object literal ์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค

๊ทธ๋ฆฌ๊ณ  ํŠน๋ณ„ํ•œ ์ด์œ ๊ฐ€ ์—†๋‹ค๋ฉด Object literal ์™ธ์˜ ๋ฐฉ๋ฒ•์œผ๋กœ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์€ ์ง€์–‘ํ•ฉ๋‹ˆ๋‹ค

๊ทธ ์ด์œ ๋Š”...

1. Property ์ถ”๊ฐ€๊ฐ€ ์–ด๋ ต์Šต๋‹ˆ๋‹ค

// Object๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ํ•˜๋‚˜์”ฉ ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•ด๋‚˜๊ฐ€์•ผ ํ•ฉ๋‹ˆ๋‹ค
const Square = new Object();
Square.width = 300;
Square.height = 300;

// ๋งŒ์•ฝ ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•˜์‹œ๋ฉด... ๊ทธ๋ƒฅ Object literal ์ด ๋‚ซ์ง€ ์•Š์„๊นŒ์š”?
const Rectangle = new Object();
Object.assign(Rectangle, { width: 300, height: 300 });

2. Object literal ํ‘œ๊ธฐ๋ฒ•์ด ์ฝ”๋“œ ๊ณต๊ฐ„์„ ๋œ ์ฐจ์ง€ ํ•ฉ๋‹ˆ๋‹ค

3. ๋ณดํŽธ์ ์ธ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์„ ๋”ฐ๋ฅด๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค
์šฐ๋ฆฌ๋Š” ํ•„์—ฐ์ ์œผ๋กœ ํ˜‘์—…์„ ํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค

๊ทธ๋ฆฌ๊ณ , ์„ฑ๋Šฅ ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค๋Š” ๊ธ€์„ ์ฝ์€ ์ ์ด ์žˆ๋Š”๋ฐ
Chrome 72 ๋ฒ„์ „ ๊ธฐ์ค€์˜ ๊ฐ„๋‹จํ•œ ํ…Œ์ŠคํŠธ์—์„œ๋Š” ํฐ ์ฐจ์ด๊ฐ€ ์—†๋Š”๊ฒƒ์œผ๋กœ ํ™•์ธ๋ฉ๋‹ˆ๋‹ค

๐Ÿ— ๋‚ด์žฅ ๊ฐ์ฒด (Built-in object)


1. ์›์‹œ(Primitive) ๊ฐ’์„ ์ œ์™ธํ•œ ๋‹ค๋ฅธ ๋‚ด์žฅ ๊ฐ์ฒด๋Š” Object ์˜ ํ•˜์œ„ ์ง‘ํ•ฉ์ž…๋‹ˆ๋‹ค

Primitives (์šฐ๋ฆฌ๋Š” Object์˜ ํ•˜์œ„ ์ง‘ํ•ฉ์ด ์•„๋‹ˆ์—์š”!)

  • string
  • number
  • boolean
  • object
  • null
  • undefined
  • symbol

2. ๋‚ด์žฅ ๊ฐ์ฒด๋Š” ๊ฝค ๋งŽ๊ธฐ ๋•Œ๋ฌธ์— ๋งํฌ๋กœ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค

๋Œ€ํ‘œ์ ์œผ๋กœ Function, Array ๋˜ํ•œ object ์˜ ํ•˜์œ„ ์ง‘ํ•ฉ ์ž…๋‹ˆ๋‹ค
function test () {}; test.a = '์˜ค์ž‰'; ๊ฐ™์ด ํ•จ์ˆ˜์— ํ”„๋กœํผํ‹ฐ ์ถ”๊ฐ€๊ฐ€ ๋˜๋Š”๊ฑด ๋‹ค๋“ค ์•„์‹คํ…๋ฐ์š”
์ด๋ ‡๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋Š”๊ฑด Function ์ด object ์˜ ํ•˜์œ„ ์ง‘ํ•ฉ์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค

3. ๋‚ด์žฅ ๊ฐ์ฒด ์ƒ์„ฑ์ž๋กœ ์ƒ์„ฑํ•œ ๊ฐ’์€ ์›์‹œ๊ฐ’๊ณผ ํƒ€์ž…์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค

// string literal ๋กœ ์›์‹œ ๋ฌธ์ž์—ด ์ƒ์„ฑ
const primitiveString = 'I am string';
typeof primitiveString // "string"
// String ์ด๋ผ๋Š” ๋‚ด์žฅ ๊ฐ์ฒด์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ์•„๋‹˜
primitiveString instanceof String; // false

const stringObject = new String('I am string');
typeof stringObject; // "object"
stringObject instanceof String; // true


// ๊ฐ์ฒด ํ•˜์œ„ ํƒ€์ž… ํ™•์ธ
Object.prototype.toString.call(stringObject); // [object String]

4. object vs Object
Object ๋Š” object ์˜ constructor ์ž…๋‹ˆ๋‹ค

5. ๋ฒˆ์™ธ, typeof null ์€ ์™œ object ์ผ๊นŒ?
typeof null === "object" ๋ผ๋Š”๊ฑด ๋‹ค๋“ค ์•Œ๊ณ  ๊ณ„์‹œ๋Š” ์‚ฌ์‹ค์ž…๋‹ˆ๋‹ค ๐Ÿ˜‡
๊ทผ๋ฐ ์™œ null ์€ object ํƒ€์ž… ์ทจ๊ธ‰์„ ๋ฐ›๊ฒŒ ๋˜์—ˆ์„๊นŒ์š”? ๐Ÿค”

๊ฐ„๋‹จํžˆ ์ดํ•ดํ•œ ๋ฐ”๋ฅผ ์ „ํ•ด๋“œ๋ฆฌ๋ฉด (์ด ๊ธ€์„ ์ฝ์—ˆ์Šต๋‹ˆ๋‹ค!)
์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ดˆ๊ธฐ ์–ธ์–ด ์„ค๊ณ„ ๋‹จ๊ณ„์—์„œ ๊ฐ’์€
์ž‘์€ type tag(1~3 ๋น„ํŠธ)์™€ ์‹ค์ œ ๊ฐ’์œผ๋กœ ๊ตฌ์„ฑ๋œ 32๋น„ํŠธ ๋‹จ์œ„๋กœ ์ €์žฅ๋˜๋Š”๋ฐ
์ด type tag ๋Š” ์ž๋ฃŒํ˜•๋ณ„๋กœ ๋ฏธ๋ฆฌ ์ง€์ •๋˜์–ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค
(object: 000, int: 1, double: 010, string: 100, boolean: 110)

null ์€ ๋ณดํŽธ์ ์œผ๋กœ ์•„๋ฌด๊ฒƒ๋„ ์—†์Œ์„ ์˜๋ฏธํ•˜๊ธฐ ๋•Œ๋ฌธ์—
object ์™€ ์•„๋ฌด๊ฒƒ๋„ ์—†์Œ (0) ์ด ๋”ํ•ด์ ธ
๋งˆ์น˜ object ํƒ€์ž…์ธ ๊ฒƒ ์ฒ˜๋Ÿผ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค ๐Ÿ’ฉ

(์ž˜๋ชป ์ดํ•ดํ•œ ๋ถ€๋ถ„์ด ์žˆ์œผ๋ฉด ๊ผญ ๋Œ“๊ธ€ ๋‚จ๊ฒจ์ฃผ์„ธ์š”!)

๊ทธ๋ฆฌ๊ณ , null type check ๋ฅผ ์œ„ํ•œ ์ฝ”๋“œ typeof null === "object" ์˜ ํ•˜์œ„ ํ˜ธํ™˜์„ ๋งž์ถ”๊ธฐ ์œ„ํ•ด
typeof null === 'null' ๋กœ ๋ณ€๊ฒฝํ•˜์ž๋Š” ์ œ์•ˆ์€ ๊ฑฐ์ ˆ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค

โญ๏ธ Property


(MDN ๋ฌธ์„œ์—์„œ ๋ฐœ์ทŒ)

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ๊ฐ์ฒด ๊ธฐ๋ฐ˜ ํŒจ๋Ÿฌ๋‹ค์ž„ ์ƒ์—์„œ ์„ค๊ณ„ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค
JavaScript is designed on a simple object-based paradigm. 

**ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋Š” ํ”„๋กœํผํ‹ฐ์˜ ์ง‘ํ•ฉ์ด๋ฉฐ**, ํ•˜๋‚˜์˜ ํ”„๋กœํผํ‹ฐ๋Š” ์ด๋ฆ„๊ณผ ๊ฐ’์˜ ์—ฐ๊ด€์„ฑ(๊ด€๊ณ„) ์ž…๋‹ˆ๋‹ค
An object is a **collection of properties**, and a property is an association between a name (or key) and a value.

๐Ÿ“ Property Name


๊ฐ์ฒด์˜ ํ”„๋กœํผํ‹ฐ ์ด๋ฆ„์€ ๊ฐ’์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•œ ๋ ˆํผ๋Ÿฐ์Šค ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค

ํ”„๋กœํผํ‹ฐ๋ช…์€ ๊ฐ์ฒด ์ปจํ…Œ์ด๋„ˆ ๋ผ๋Š” ๊ณณ์— ๋ณด๊ด€๋˜์–ด ์žˆ๋Š”๋ฐ
์ด ํ”„๋กœํผํ‹ฐ๋ช…์„ ํ†ตํ•ด, ํ”„๋กœํผํ‹ฐ ๊ฐ’์ด ์ €์žฅ๋œ ํฌ์ธํ„ฐ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๊ฐ’์„ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
(๊ฐ์ฒด ์ปจํ…Œ์ด๋„ˆ๋Š” Host ํ™˜๊ฒฝ (๋ธŒ๋ผ์šฐ์ €) ์— ๋”ฐ๋ผ ๊ทธ ๋™์ž‘ ๋ฐฉ์‹์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค)

Property Name ์— ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‘ ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค

1. . ์—ฐ์‚ฐ์ž

2. [ ] ์—ฐ์‚ฐ์ž

  • ํ‚ค ์ ‘๊ทผ ์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค
  • UTF-8 / Unicode ํ˜ธํ™˜ ๋ฌธ์ž์—ด์ด๋ฉด ๋ชจ๋‘ ํ”„๋กœํผํ‹ฐ๋ช…์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
    • const a = {}; a['Awesome-Property!'] = 1; ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
  • ๋งŒ์•ฝ ๋ฌธ์ž์—ด ์ด์™ธ์˜ ๋‹ค๋ฅธ ์›์‹œ ๊ฐ’์„ ์‚ฌ์šฉํ•˜๋ฉด, ์šฐ์„  ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜๋œ ๋’ค์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค
const obj = {};
obj[null] = 'NULL'
obj[true] = 'TRUE'
obj[obj] = 'Object'

console.log(obj['null']) // 'NULL'
console.log(obj['true']) // 'TRUE'
console.log(obj['[object Object]']) // 'Object'

3. ๊ณ„์‚ฐ๋œ ํ”„๋กœํผํ‹ฐ๋ช… (Computed property name)

  • ES6 ์—์„œ ์ถ”๊ฐ€๋œ ๊ธฐ๋Šฅ
  • ๊ฐ์ฒด ๋ฆฌํ„ฐ๋Ÿด ๋‚ด๋ถ€์—์„œ [ ] ๋กœ ๊ฐ์‹ผ ๋’ค ๊ณ„์‚ฐ๋œ ํ”„๋กœํผํ‹ฐ๋ช…์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค
const meow = 'MEOW';
const obj = {
  [meow]: 'NYAN',
  [meow+meow]: 'NYAN NYAN',
};

console.log(obj['MEOW']) // 'NYAN'
console.log(obj['MEOWMEOW']) // 'NYANNYAN'

// ES6 Symbol ์—์„œ๋„ ์‚ฌ์šฉ๋จ
const objWithSymbol = {
  [Symbol.Private]: "Private"
};

console.log(objWithSymbol[Symbol.Private]) // "Private"

๐Ÿ—ƒ Property Value


์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ํ—ˆ์šฉ๋˜๋Š” ๊ฐ’์€ ๋ชจ๋‘ ํ”„๋กœํผํ‹ฐ์˜ ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

๐Ÿ– Descriptor


ํ”ํžˆ Property Descriptor ๋ผ๊ณ  ๋ถ€๋ฅด๋Š”
ํ”„๋กœํผํ‹ฐ์˜ ํŠน์„ฑ์— ๋Œ€ํ•œ ์„œ์ˆ ์ž๋Š” ES5 ์ด์ „์—๋Š” ํ™•์ธํ•  ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค

ํ˜„์žฌ๋Š” descriptor ์˜ ๊ฐ’์„ ์กฐํšŒํ•˜๊ณ , ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค

๊ทธ๋ฆฌ๊ณ , object ์˜ ๋ชจ๋“  property ๋Š”
๋„ค ๊ฐ€์ง€์˜ Data Descriptor ์™€ ๋‘ ๊ฐ€์ง€์˜ Accessor Descriptor ์†์„ฑ์„ ๊ฐ–์Šต๋‹ˆ๋‹ค

์ด๋ ‡๊ฒŒ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

const obj = { a: 1 };
console.log(Object.getOwnPropertyDescriptor(obj, 'a'));

// console.log
// {value:1, writable: true, enumerable: true, configurable: true}

๊ทธ๋Ÿผ, ๋จผ์ € Data Descriptor ์— ๋Œ€ํ•ด ์•Œ์•„ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค

๐Ÿ– Property - Data Descriptor


1. value

ํ”„๋กœํผํ‹ฐ์˜ ๊ฐ’์„ ์ •์˜ ํ•ฉ๋‹ˆ๋‹ค

  • Type: ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ํ—ˆ์šฉ ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  ๊ฐ’
  • Default: undefined

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ํ—ˆ์šฉ ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  ๊ฐ’์„ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

2. writable

ํ”„๋กœํผํ‹ฐ ๊ฐ’์˜ ์ˆ˜์ • ๊ฐ€๋Šฅ ์—ฌ๋ถ€์— ๋Œ€ํ•ด ์ •์˜ ํ•ฉ๋‹ˆ๋‹ค

  • Type: boolean
  • Default: false

๋งŒ์•ฝ writable: false ์ธ ๊ฒฝ์šฐ์—๋Š” ์•„๋ž˜ ๋‘ ๊ฐ€์ง€ ์กฐ๊ฑด ํ•˜์—์„œ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค

  • ์—„๊ฒฉ ๋ชจ๋“œ(use strict): throw Error
  • ๋น„ ์—„๊ฒฉ ๋ชจ๋“œ: (์กฐ์šฉํžˆ๐Ÿคซ) ๊ฐ’ ํ• ๋‹น ์‹คํŒจ
const cat = {};
Object.defineProperty(cat, 'name', {
  value: 'DoonDoon',
  writable: false, // ํ•œ ๋ฒˆ ์ •ํ•œ ์ด๋ฆ„์„ ๋ฐ”๊ฟ€ ์ˆ˜ ์—†์–ด์š”!
  configurable: true,
  enumerable: true,
});

cat.name = 'TT'; // ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค (์กฐ์šฉํžˆ ์‹คํŒจ๐Ÿคซ)
console.log(cat.name) // DoonDoon

3. enumerable

ํ”„๋กœํผํ‹ฐ ์—ด๊ฑฐ ๊ฐ€๋Šฅ ์—ฌ๋ถ€๋ฅผ ์ •์˜ ํ•ฉ๋‹ˆ๋‹ค

  • Type: boolean
  • Default: false

๊ฐ์ฒด ํ”„๋กœํผํ‹ฐ๋ฅผ ์—ด๊ฑฐํ•˜๋Š” for ... in ๋ฌธ์ด๋‚˜,
Object.keys(), Object.values(), Object.entries() ๋“ฑ์˜ ํ•จ์ˆ˜์—์„œ
ํ”„๋กœํผํ‹ฐ๊ฐ€ ์—ด๊ฑฐ๋˜์ง€ ์•Š๋„๋ก ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

const cat = {}
Object.defineProperties(cat, {
  name: {
    value: 'DoonDoon',
    writable: true,
    configurable: true,
    enumerable: true,
  },
  age: {
    value: 3,
    writable: true,
    configurable: true,
    enumerable: false, // ๋‚˜์ด๋Š” ์•Œ๋ฆฌ๊ณ  ์‹ถ์ง€ ์•Š์•„์š” ๐Ÿฑ
  },
});

for (let key in cat) { console.log(key) } // name
Object.keys(cat) // ["name"]
Object.values(cat) // ["DoonDoon"]
Object.entries(cat) // [["name", "DoonDoon"]]

4. configurable

Property Descriptor ์˜ ์ˆ˜์ • ๊ฐ€๋Šฅ ์—ฌ๋ถ€๋ฅผ ์ •์˜ ํ•ฉ๋‹ˆ๋‹ค

  • Type: boolean
  • Default: false

๋งŒ์•ฝ false ์ผ ๊ฒฝ์šฐ์—๋Š” writable: true ๋ฅผ writable: false ๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ ์™ธ์—
๋ชจ๋“  ์„ค์ • ๋ณ€๊ฒฝ์ด ๊ธˆ์ง€๋ฉ๋‹ˆ๋‹ค (writable์ด ์ตœ์ดˆ์— true ์˜€๋‹ค๋Š” ์ „์ œ ํ•˜์—์„œ)
๊ทธ๋ฆฌ๊ณ  writable ๋˜ํ•œ ํ•œ ๋ฒˆ false ๋กœ ๋ณ€๊ฒฝ๋œ ๋’ค์—๋Š” ๋” ์ด์ƒ ๋ณ€๊ฒฝํ• ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค!

๋˜ํ•œ, ํ”„๋กœํผํ‹ฐ๋ฅผ ์‚ญ์ œํ•˜๋Š” ๊ฒƒ ๊นŒ์ง€๋„ ๋ถˆ๊ฐ€๋Šฅ ํ•ฉ๋‹ˆ๋‹ค.
ํ•œ ๋งˆ๋””๋กœ, configurable: false ๋Š” ๋Œ์•„์˜ฌ ์ˆ˜ ์—†๋Š” ๊ฐ•์„ ๊ฑด๋„ˆ๋Š” ์…ˆ ์ž…๋‹ˆ๋‹ค ๐Ÿ›ถ

const cat = {};

Object.defineProperties(cat, {
  name: {
    value: 'DoonDoon',
    writable: true,
    enumerable: true,
    configurable: false, // name ํ”„๋กœํผํ‹ฐ์— ๋Œ€ํ•œ ์„ค์ •์„ ๋” ์ด์ƒ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค
  },
  age: {
    value: 3,
    writable: true,
    enumerable: true,
    configurable: true,
  },
});

// writable ์€ false ์—์„œ true ๋กœ ํ•œ ๋ฒˆ๋งŒ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๋Œ์•„์˜ฌ ์ˆ˜ ์—†๋Š” ๊ฐ•์„ ๊ฑด๋„ˆ๋Š” ๊ฑฐ์ฃ  ๐Ÿ›ถ
Object.defineProperty(cat, 'name', { writable: false, });

try {
  Object.defineProperty(cat, 'name', { writable: true, }); // ์•ˆ ๋ฐ”๋€๋‹ค! ๊ทธ๋ฆฌ๊ณ  ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒ ํ•ฉ๋‹ˆ๋‹ค
} catch (e) {
  console.error(e); // [TypeError: Cannot redefine property: name]
}

delete cat.name // ์•ˆ ์ง€์›Œ์ ธ!

console.log(cat.name) // "DoonDoon"
console.log(Object.getOwnPropertyDescriptor(cat, 'name')); // { value: 'DoonDoon', writable: false, enumerable: true, configurable: false }

๐Ÿ– Property - Accessor Descriptor


ํ”„๋กœํผํ‹ฐ๊ฐ€ [Getter] || [Setter] ๊ฐ€ ๋  ์ˆ˜ ์žˆ๊ฒŒ ์ •์˜ํ•œ ๊ฒƒ์„ ์ ‘๊ทผ ์„œ์ˆ ์ž๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค
์ ‘๊ทผ ์„œ์ˆ ์ž๋Š” 4 ๊ฐ€์ง€์˜ ์†์„ฑ์„ ๊ฐ–์Šต๋‹ˆ๋‹ค: configurable, enumerable, get, set
(๊ฐ’์— ๊ด€๋ จ๋œ ์†์„ฑ์ธ value, writable ์†์„ฑ์€ ๋ฌด์‹œ๋ฉ๋‹ˆ๋‹ค)

[Getter] / [Setter] ๋ฅผ ์–ธ์ œ ์“ฐ๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ์žฅ๋‹จ์ ์€ ๋ฌด์—‡์ธ์ง€์— ๋Œ€ํ•œ ๋…ผ์˜๋Š”
์ข‹์€ ๊ธ€๋“ค๋กœ ๋Œ€์‹ ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค ๐Ÿ™

1. [Getter]

ํ”„๋กœํผํ‹ฐ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ์‹คํ–‰๋  ํ•จ์ˆ˜๋กœ ๋ฐ”์ธ๋”ฉ ํ•ฉ๋‹ˆ๋‹ค
์ผ๋ฐ˜์ ์œผ๋กœ ์–ด๋– ํ•œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

๋ณดํ†ต์€ ๊ณ„์‚ฐ๋œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•˜๊ฑฐ๋‚˜,
๋‚ด๋ถ€ ๋ณ€์ˆ˜(ํ•„๋“œ)๋ฅผ ์ง์ ‘ ๋…ธ์ถœํ•˜๊ณ  ์‹ถ์ง€ ์•Š์„๋•Œ ์‚ฌ์šฉํ•˜์ง€๋งŒ
์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” accessor ๊ฐ€ ์กด์žฌํ•˜๊ธฐ ์•Š๊ธฐ ๋•Œ๋ฌธ์—

์šฐ์„ ์€ ๊ณ„์‚ฐ๋œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š”๊ฒŒ ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค ๐Ÿค”

[Getter] ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

const cat = {
  _name: 'DoonDoony',
  get name() {
    return `${this._name} the Cat`
  },
}

// ํ”„๋กœํผํ‹ฐ ์ ‘๊ทผ์„ ํ•ด๋„, ์ •์˜๋œ ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ  ๊ฐ’์ด ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค
console.log(cat.name) // 'DoonDoony the Cat'

[Getter] ๋Š” ์ด๋Ÿฐ ํŠน์ง•๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค!

1. delete ์—ฐ์‚ฐ์ž๋ฅผ ์ด์šฉํ•ด ์‚ญ์ œ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค

const obj = { get name() { return 'DoonDoony' } };
console.log(obj.name); // "DoonDoony"
delete obj.name;
console.log(obj.name); // undefined

2. defineProperty ๋ฅผ ์ด์šฉํ•ด ๊ฐ์ฒด์— ์ƒˆ ํ”„๋กœํผํ‹ฐ๋กœ ์ถ”๊ฐ€๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค

const obj = { _name: 'DoonDoony' }
Object.defineProperty(obj, 'name', {
    get: function() {
        return `${this._name} the Cat!`
    },
    enumerable: true,
    configurable: true,
})
console.log(obj.name) // "DoonDoony the Cat!"

3. ๊ณ„์‚ฐ๋œ ํ”„๋กœํผํ‹ฐ(Computed Property) ์ด๋ฆ„์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

const prefix = 'name'
const obj = { 
  get [prefix] () { 
    return 'Getter is called with prefix' 
  }
}

// prefix์™€ ๊ฐ™์€ ๋ฌธ์ž์—ด์ธ "name" ํ”„๋กœํผํ‹ฐ๋ช… ์œผ๋กœ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
console.log(obj.name) // "Getter is called with prefix"

4. lazy evaluation ์ด ๊ฐ€๋Šฅ ํ•ฉ๋‹ˆ๋‹ค

[Getter] ๋Š” ํ”„๋กœํผํ‹ฐ์— ์ ‘๊ทผํ•˜๊ธฐ ์ „๊นŒ์ง€๋Š” ๊ฐ’์„ ๊ณ„์‚ฐํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค

getter_lazy_evaluation.gif
๊ฐ’์ด ์•„๋‹Œ [Getter] ๋กœ ํ‰๊ฐ€๋ฉ๋‹ˆ๋‹ค!

5. [Getter] ๋ฐ˜ํ™˜๊ฐ’์˜ ์บ์‹ฑ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค

[Getter] ๊ฐ€ ํ˜ธ์ถœ ์ „๊นŒ์ง€ ํ‰๊ฐ€๋˜์ง€ ์•Š๋Š” ํŠน์ง•๊ณผ
์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ๋ณ€์ˆ˜์— ๋‹ด๋Š” ๊ฐ’์€ ํ•œ ๋ฒˆ๋งŒ ๊ณ„์‚ฐ๋œ๋‹ค๋Š” ์ ์„ ํ™œ์šฉํ•˜์—ฌ
[Getter] ๋ฅผ ๊ฐ’์œผ๋กœ ํ‰๊ฐ€ํ•ด caching ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

์ผ๋ฐ˜์ ์ธ ํ‚ค - ๋ฐธ๋ฅ˜ ํ˜•ํƒœ์˜ ํ”„๋กœํผํ‹ฐ๊ฐ€ ์•„๋‹ˆ๋ผ
[Getter] ๋กœ ๊ตฌํ˜„ํ•˜๋Š” ์ด์œ ๋Š” ์ง€์—ฐํ‰๊ฐ€๋ฅผ ํ™œ์šฉํ•˜๋Š” ๊ฑด๋ฐ์š”
MDN ๋ฌธ์„œ์—์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ๊ฒฝ์šฐ์— ์œ ์šฉํ•˜๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค

  • ๊ฐ’์˜ ๊ณ„์‚ฐ ๋น„์šฉ์ด ํฐ ๊ฒฝ์šฐ. (RAM์ด๋‚˜ CPU ์‹œ๊ฐ„์„ ๋งŽ์ด ์†Œ๋ชจํ•˜๊ฑฐ๋‚˜, worker thread๋ฅผ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜, ์›๊ฒฉ ํŒŒ์ผ์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋“ฑ)
  • ๊ฐ’์ด ๋‹น์žฅ์€ ํ•„์š”ํ•˜์ง€ ์•Š์ง€๋งŒ ๋‚˜์ค‘์— ์‚ฌ์šฉ๋˜์–ด์•ผ ํ•  ๊ฒฝ์šฐ, ํ˜น์€ ์ ˆ๋Œ€๋กœ ์ด์šฉ๋˜์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ๋Š” ๊ฒฝ์šฐ.
  • ๊ฐ’์ด ์—ฌ๋Ÿฌ ์ฐจ๋ก€ ์ด์šฉ๋˜์ง€๋งŒ ์ ˆ๋Œ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์•„ ๋งค๋ฒˆ ๋‹ค์‹œ ๊ณ„์‚ฐํ•  ํ•„์š”๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ, ํ˜น์€ ๋‹ค์‹œ ๊ณ„์‚ฐ๋˜์–ด์„œ๋Š” ์•ˆ ๋˜๋Š” ๊ฒฝ์šฐ.

์ฝ”๋“œ๋ฅผ ํ•œ ๋ฒˆ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค ๐Ÿ˜Ž

const cat = {
  get name() {
    delete this.name; // 1
    return this.name = 'DoonDoony๐Ÿˆ'; // 2
  }
}

console.log(cat.name); // 3
  1. ์ด ๋•Œ cat.name ์ด๋ผ๋Š” [Getter] ๋ฅผ cat ๊ฐ์ฒด์—์„œ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค
  2. cat ๊ฐ์ฒด์— name ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค (name ์€ ์ด ๋•Œ ๋ถ€ํ„ฐ๋Š” [Getter] ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค)
  3. cat.name [Getter] (์•„์ง์€ [Getter]) ๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ , cat.name ํ”„๋กœํผํ‹ฐ๋กœ ๋ณ€๊ฒฝ ๋˜์–ด ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค

๋ฐ˜ํ™˜๊ฐ’์ด this.name = 'DoonDoony๐Ÿˆ' ์™€ ๊ฐ™์ด ํ• ๋‹น๋ฌธ(Assignment) ์ž„์—๋„
์ •์ƒ์ ์œผ๋กœ 'DoonDoony' ๋ผ๋Š” ๋ฌธ์ž์—ด์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ด์œ ๋Š”
์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ํ• ๋‹น๋ฌธ์€ ํ• ๋‹นํ•œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค
(๋‹จ, var, let, const ํ‚ค์›Œ๋“œ ์—†์ด ํ• ๋‹นํ•œ ๊ฒฝ์šฐ)

์œ„์˜ ์˜ˆ์ œ๋Š” ๋‹จ์ˆœํžˆ ๋ฌธ์ž์—ด์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์˜ˆ์ œ๋ผ ์‹ค์šฉ์ ์ด์ง€ ์•Š์•„ ๋ณด์ด์ง€๋งŒ
์‹ค์ œ๋กœ ๊ต‰์žฅํžˆ ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ๊ณ„์‚ฐ์ด ํ•„์š”ํ•œ๋ฐ, ์ฝ”๋“œ ์‹คํ–‰ ์‹œ์ ์— ๋ฏธ๋ฆฌ ๊ณ„์‚ฐํ•ด๋‘˜ ํ•„์š”๊ฐ€ ์—†๋‹ค๋ฉด
์ด์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์ด ์œ ์šฉํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์กฐ๊ธˆ์€ ๊ทธ๋Ÿด์‹ธํ•ด ๋ณด์ด๋Š” ์˜ˆ์ œ ์ฝ”๋“œ ์ž…๋‹ˆ๋‹ค

// ์ฝ”๋“œ๋Š” ๋ณต์‚ฌํ•ด์„œ ๋ธŒ๋ผ์šฐ์ € ์ฝ˜์†”์ฐฝ์—์„œ ์‹คํ–‰ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค
// Required only nodejs
// const { performance } = require('perf_hooks');

const obj = {
  get memoizedGetter() {
    delete this.memoizedGetter;
    return this.memoizedGetter = [...Array(5e+7).keys()].reduce((prev, num) => prev + num, 0);
  },
  get commonGetter() {
    return [...Array(5e+7).keys()].reduce((prev, num) => prev + num, 0);
  },
};

const measureExecutionTime = (functionName, fn) => {
  const start = performance.now();
  fn();
  return `[${functionName}] Elasped Time: ${performance.now() - start} ms`;
};

console.log(measureExecutionTime('Memoized Getter Test1', () => obj.memoizedGetter));
console.log(measureExecutionTime('Memoized Getter Test2', () => obj.memoizedGetter));
console.log(measureExecutionTime('Memoized Getter Test3', () => obj.memoizedGetter));

console.log(measureExecutionTime('Common Getter Test1', () => obj.commonGetter));
console.log(measureExecutionTime('Common Getter Test2', () => obj.commonGetter));
console.log(measureExecutionTime('Common Getter Test3', () => obj.commonGetter));

์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•œ ๊ฒฐ๊ณผ์ž…๋‹ˆ๋‹ค
memoized_getter.gif

์ด์ œ ์กฐ๊ธˆ ๊ดœ์ฐฎ์•„ ๋ณด์ด์‹œ๋‚˜์š”? (์•„๋‹ˆ์‹œ๋ผ๋ฉด ์–ด์ฉ” ์ˆ˜ ์—†์ง€๋งŒ ๐Ÿ˜ญ)

๊ทผ๋ฐ ๋ฌธ๋“ ์ด๋Ÿฐ ์ƒ๊ฐ์ด ๋“ญ๋‹ˆ๋‹ค!
[Getter] ๋ฅผ ์ผ๋ฐ˜์ ์ธ ํ”„๋กœํผํ‹ฐ๋กœ ๋ณ€๊ฒฝํ•ด๋ฒ„๋ฆฌ๋ฉด, ๋ฎ์–ด ์“ธ ์ˆ˜ ์žˆ์ง€ ์•Š์„๊นŒ?
์ƒ์ˆ˜์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์€๋ฐ...๐Ÿค”

์‹ค์ œ๋กœ ์•„๋ž˜์ฒ˜๋Ÿผ Property Descriptor ๋ฅผ ํ™•์ธํ•ด๋ณด๋ฉด
์‹คํ–‰ ์ „ ํ›„ ํ”„๋กœํผํ‹ฐ์˜ ์„ฑ๊ฒฉ์ด ์™„์ „ํžˆ ๋‹ค๋ฅธ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
(๊ทธ๋ฆฌ๊ณ  ์ƒˆ ๊ฐ’์ด ํ• ๋‹น ๋ฉ๋‹ˆ๋‹ค)

const cat = {
  get name () {
    // ์‚ญ์ œ ์ „์—๋Š” getter ์˜ Property Descriptor ์†์„ฑ ๊ฐ’์ด ํ™•์ธ๋ฉ๋‹ˆ๋‹ค
    console.log(Object.getOwnPropertyDescriptor(this, 'name')); // { get: [Getter], set: undefined, enumerable: true, configurable: true }
    delete this.name;
    return this.name = 'DoonDoony';
  }
};

console.log(cat.name); // "DoonDoony"
// getter ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด, ํ”„๋กœํผํ‹ฐ๊ฐ€ ์ง€์›Œ์ง€๊ณ , ์ผ๋ฐ˜์ ์ธ ๊ฐ’ ํ”„๋กœํผํ‹ฐ์˜ ์†์„ฑ์ด ํ™•์ธ๋ฉ๋‹ˆ๋‹ค.
console.log(Object.getOwnPropertyDescriptor(cat, 'name')); // { value: 'DoonDoony', writable: true, enumerable: true, configurable: true }
cat.name = 'TT'
console.log(cat.name) // "TT", ์ด๋Ÿฐ! ๋‘”๋‘๋‹ˆ๊ฐ€ ํ‹ฐํ‹ฐ๋กœ ๋ณ€ํ–ˆ์–ด์š” ๐Ÿ˜‹

๊ทธ๋Ÿผ [Getter] ์˜ ์ง€์—ฐ ํ‰๊ฐ€์˜ ์žฅ์ ์„ ์‚ด๋ฆฌ๋ฉด์„œ
๊ฐ’์€ ์ƒ์ˆ˜์ฒ˜๋Ÿผ ๋ณ€ํ•˜์ง€ ์•Š๊ฒŒ ์œ ์ง€ํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•  ์ˆ˜ ์žˆ์„๊นŒ์š”?

const obj = {
  get hugeValue () {
    console.log(Object.getOwnPropertyDescriptor(this, 'hugeValue')); // {get: [Getter], set: undefined, enumerable: true, configurable: true}
    delete this.hugeValue;
    const cachedValue = [...Array(5e+7).keys()].reduce((prev, curr) => prev + curr, 0);
    return Object.defineProperty(this, 'hugeValue', {
      value: cachedValue,
      writable: false,
      configurable: false,
      enumerable: true,
    }).hugeValue 
  },
  name: 'HugeValueInside',
};

console.log(obj.hugeValue); // 1249999975000000
obj.hugeValue = '๋ณ€ํ•ด๋ผ ์–!';
console.log(obj.hugeValue); // 1249999975000000
console.log(Object.getOwnPropertyDescriptor(obj, 'hugeValue')); // {value: 1249999975000000, writable: false, enumerable: true, configurable: false}

์บ์‹ฑํ•ด์•ผํ•  ํฐ ๊ฐ’์„ ๋ณ€์ˆ˜์— ๋‹ด๊ณ , ๋ฐ˜ํ™˜ ์‹œ์ ์— ์œ„์™€ ๊ฐ™์ด ์ •์˜ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค!

obj.hugeValue ๋Š” ์ผ๋ฐ˜ ํ”„๋กœํผํ‹ฐ๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ์ง€๋งŒ
writable: false, configurable: false ์†์„ฑ์œผ๋กœ ์ธํ•ด ์‚ญ์ œ์™€ ๋ณ€๊ฒฝ์ด ๋ถˆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค

์กฐ๊ธˆ ์‚ฌ์กฑ๐Ÿ์„ ๋‹ฌ์ž๋ฉด

return Object.defineProperty(...).hugeValue ์ฒ˜๋Ÿผ ํ”„๋กœํผํ‹ฐ๋ฅผ ๋ช…์‹œํ•˜์ง€ ์•Š์œผ๋ฉด

Object.defineProperty์˜ ๋ฐ˜ํ™˜๊ฐ’์€ ์ƒˆ๋กœ ์ •์˜๋œ ๊ฐ์ฒด์ด๊ธฐ ๋•Œ๋ฌธ์—

{ hugeValue: 1249999975000000, name: 'HugeValueInside' } ์™€ ๊ฐ™์ด ๊ฐ์ฒด๊ฐ€ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค

2. [Setter]
ํ”„๋กœํผํ‹ฐ์— ๊ฐ’์„ ํ• ๋‹นํ•  ๋•Œ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค
์ผ๋ฐ˜์ ์œผ๋กœ ๋ฐ˜ํ™˜ ๊ฐ’์ด ์—†์Šต๋‹ˆ๋‹ค

๋งŒ์•ฝ ๋ฐ˜ํ™˜ ๊ฐ’์„ ์„ค์ •ํ•˜๋ฉด, ๋‹น์—ฐํ•˜์ง€๋งŒ ๊ฐ’์ด ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค
๊ทธ๋ฆฌ๊ณ  ๊ทธ ๋™์ž‘์€ Assignment Operator ๊ฐ€ ๋™๋ฐ˜ํ•˜๋Š”
Side-Effect ์™€ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค! (Operator: x = y)

Simple Assignment์—์„œ์˜ ๋ช…์„ธ์— ์˜ํ•ด,
๋‹จ์ˆœํ•œ(const, let, var ์—†๋Š”) = ์—ฐ์‚ฐ์ž ์‚ฌ์šฉ์€
์šฐ๋ณ€์˜ ํ• ๋‹น ์‹์˜ ๊ฐ’(์‹์„ ์‹คํ–‰ํ•˜๊ณ  ํ‰๊ฐ€๋œ ๊ฐ’)์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค

๋ฐฐ์›€์ด ๊นŠ์„์ˆ˜๋ก, ๊ฐ„๊ฒฐํ•˜๊ณ  ๋ช…๋ฃŒํ•œ ์„ค๋ช…์ด ๊ฐ€๋Šฅ ํ•˜๋‹ค๋Š”๋ฐ
๋ฐฐ์›€์ด ๋ถ€์กฑํ•ด์„œ ๋ง์ด ๋„ˆ๋ฌด ์–ด๋ ต์Šต๋‹ˆ๋‹ค ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค๐Ÿ˜ญ

์ดํ•ด๋ฅผ ๋•๊ธฐ ์œ„ํ•ด ์ฝ”๋“œ๋กœ ์„ค๋ช… ํ• ๊ฒŒ์š”

const obj = {
  set someValue(a) {
    return a
  }
}

const unexpected = obj.someValue = 'Surprise!';
console.log(unexpected) // 'Surprise!';

๋ง‰์ƒ ์ฝ”๋“œ๋ฅผ ๋ณด๋‹ˆ๊นŒ ๋ณ„ ๊ฑฐ ์•„๋‹ˆ์ฃ ?
[Setter] ๋Š” ํ• ๋‹น ์‹์œผ๋กœ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š”๋ฐ, ๊ทธ ํ•จ์ˆ˜์— ๋ฐ˜ํ™˜ ๊ฐ’์ด ์žˆ์œผ๋ฉด
์˜๋„์น˜ ์•Š์€ Side-Effect ๋ฅผ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค ๋ผ๋Š” ๊ฒƒ์„ ์„ค๋ช…ํ•˜๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค!

"๋งŒ์•ฝ" ์— ๋Œ€ํ•œ ์–˜๊ธฐ๊ฐ€ ๋„ˆ๋ฌด ๊ธธ์–ด ์กŒ๋Š”๋ฐ
๋‹ค์‹œ [Setter]์— ๋Œ€ํ•ด ์„ค๋ช…ํ•˜์ž๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฌธ๋ฒ•์œผ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค

const cat = {
  _name: undefined,
  set name(newName) {
    this._name = newName;
  },
  call() {
    console.log(this._name);
  },
};

cat.name = 'DoonDoony';
cat.call(); // 'DoonDoony'

ํ”„๋กœํผํ‹ฐ์— ๊ฐ’์„ ํ• ๋‹นํ•˜๋Š” ์ž‘์—…์€ ๋‚ด๋ถ€์ ์œผ๋กœ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ์ˆœ์„œ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค

๐Ÿ™†โ€โ™€๏ธ์ ‘๊ทผํ•˜๋Š” ํ”„๋กœํผํ‹ฐ๊ฐ€ ์žˆ๋‹ค๋ฉด

  1. ์ ‘๊ทผํ•œ ํ”„๋กœํผํ‹ฐ๊ฐ€ Accessor Descriptor(Getter ๋˜๋Š” Setter) ๋ผ๋ฉด?
    โ†’ [Setter] ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค, ๋งŒ์•ฝ [Getter] ๋ผ๋ฉด ๊ฐ’ ํ• ๋‹น์€ ๋ฌด์‹œ๋ฉ๋‹ˆ๋‹ค
  2. Accessor Descriptor ์†์„ฑ์ด ์—†์œผ๋ฉด์„œ, writable: false ๋ผ๋ฉด?
    โ†’ ์กฐ์šฉํžˆ ์‹คํŒจํ•˜๊ฑฐ๋‚˜ "use strict" ๋ชจ๋“œ์—์„œ๋Š” TypeError ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค
  3. 1๋ฒˆ, 2๋ฒˆ ๋ชจ๋‘ ํ•ด๋‹น๋˜์ง€ ์•Š์œผ๋ฉด, ํ”„๋กœํผํ‹ฐ์— ๊ฐ’์„ ์„ธํŒ…ํ•ฉ๋‹ˆ๋‹ค

๐Ÿ™…โ€โ™€๏ธ์ ‘๊ทผํ•˜๋Š” ํ”„๋กœํผํ‹ฐ๊ฐ€ ์—†๋‹ค๋ฉด
์˜ค๋ธŒ์ ํŠธ์™€ ์—ฐ๊ฒฐ๋œ ์ƒ์œ„ [[Prototype]] ์ฒด์ธ์„ ์ˆœํšŒํ•ฉ๋‹ˆ๋‹ค.
์ด๋ฅผ ๋‘ ๊ฐ€์ง€ ๊ฒฝ์šฐ๋กœ ๋‚˜๋ˆ„์–ด ์ƒ๊ฐํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

๐Ÿ™…โ€โ™€๏ธ๋ชจ๋“  [[Prototype]] ์ฒด์ธ์—์„œ ํ”„๋กœํผํ‹ฐ๊ฐ€ ๋ฐœ๊ฒฌ๋˜์ง€ ์•Š์„ ๊ฒฝ์šฐ

  1. Object extensible ํ•˜๋‹ค๋ฉด, ์ง์† ํ”„๋กœํผํ‹ฐ(Directly Present)๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ฐ’์„ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค
  2. Object extensible(Object.preventExtensions(), Object.isExtensible()) ํ•˜์ง€ ์•Š๋‹ค๋ฉด
    โ†’ ์ƒˆ ํ”„๋กœํผํ‹ฐ๋ฅผ ๋งŒ๋“ค์ง€ ๋ชปํ•˜๊ณ  TypeError ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค

๐Ÿ“Œ์—ฌ๊ธฐ์„œ Object extensible ํ•˜๋‹ค๋Š” ๊ฒƒ์€ Object.isExtensible(obj) === true ๋ฅผ ์˜๋ฏธ ํ•ฉ๋‹ˆ๋‹ค

  • ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์˜ค๋ธŒ์ ํŠธ๋Š”, ํ”„๋กœํผํ‹ฐ์˜ ์ถ”๊ฐ€๊ฐ€ ๊ฐ€๋Šฅํ•œ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค
  • ํ™•์žฅ ๋ถˆ๊ฐ€๋Šฅํ•œ ์˜ค๋ธŒ์ ํŠธ๋Š”, ํ”„๋กœํผํ‹ฐ์˜ ์ถ”๊ฐ€๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•œ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค
    โ†’ ๋‹จ, ํ”„๋กœํผํ‹ฐ์˜ ์‚ญ์ œ๋Š” ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค
    โ†’ ์ด๋ฏธ ์กด์žฌํ•˜๋˜ ํ”„๋กœํผํ‹ฐ์— ๋Œ€ํ•œ ์ˆ˜์ • ๋˜ํ•œ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค
    โ†’ ์˜ค๋ธŒ์ ํŠธ์˜ __proto__ ์— ๊ฐ’์„ ์ถ”๊ฐ€, ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ ๋˜ํ•œ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค

๋‹ค์Œ์€ ๊ฐ„๋‹จํ•œ Object extensible ์— ๋Œ€ํ•œ ์˜ˆ์ œ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค

'use strict';

const parentCat = {
  name: 'DoonDoon',
  age: 10,
};

const childCat = Object.create(parentCat);
Object.assign(childCat, { name: 'DoonDoony', age: 3, favorite: 'Red Ball' });

// childCat ์„ ํ™•์žฅ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค
Object.preventExtensions(childCat);
// childCat ์ด ํ™•์žฅ ๋ถˆ๊ฐ€๋Šฅํ•œ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค
console.log(Object.isExtensible(childCat)); // false

// ๊ธฐ์กด์˜ ํ”„๋กœํผํ‹ฐ๋ฅผ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค
childCat.favorite = 'Box';
console.log(childCat.favorite); // Box

// ๊ธฐ์กด์˜ ํ”„๋กœํผํ‹ฐ๋ฅผ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค
delete childCat.favorite; // true
console.log(childCat.favorite); // undefined

// [[Prototype]] ์— ์ƒˆ ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค
childCat.__proto__.gender = 'male';
console.log(Object.getPrototypeOf(childCat)); // { name: 'DoonDoon', age: 10, gender: 'male' }

childCat.gender = 'male'; // ๐Ÿ™…โ€โ™€๏ธ TypeError!

Object extensible ์— ๋Œ€ํ•œ ์ฐธ๊ณ  ๋งํฌ

๐Ÿ™†โ€โ™€๏ธ์ƒ์œ„ [[Prototype]] ์ฒด์ธ์—์„œ ํ”„๋กœํผํ‹ฐ๊ฐ€ ๋ฐœ๊ฒฌ๋œ ๊ฒฝ์šฐ

  1. ํ”„๋กœํผํ‹ฐ๊ฐ€ ์“ฐ๊ธฐ ๊ฐ€๋Šฅ(writable: true)ํ•œ ๊ฒฝ์šฐ ์ง์† ํ”„๋กœํผํ‹ฐ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ฐ’์„ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค
  2. ํ”„๋กœํผํ‹ฐ๊ฐ€ ์“ฐ๊ธฐ ๋ถˆ๊ฐ€๋Šฅ(writable: false)ํ•œ ๊ฒฝ์šฐ ์•„๋ฌด ์ผ๋„ ์ผ์–ด๋‚˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค
    ์—„๊ฒฉ ๋ชจ๋“œ์˜ ๊ฒฝ์šฐ TypeError ๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ , ์•„๋‹Œ ๊ฒฝ์šฐ์—” ์กฐ์šฉํžˆ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค

  3. ํ”„๋กœํผํ‹ฐ๊ฐ€ [Setter] ์ธ ๊ฒฝ์šฐ ํ•ญ์ƒ ์ด ์„ธํ„ฐ๊ฐ€ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ์„ธํ„ฐ๋ฅผ ๋ฎ์–ด์“ฐ๋Š” ์ผ์€ ์ผ์–ด๋‚˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค
    ๋งŒ์•ฝ ์ด ์„ธํ„ฐ๋ฅผ ๋ฎ์–ด์“ฐ๋ ค๋ฉด, Object.defineProperty ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค

    ์ถ”๊ฐ€์ ์œผ๋กœ, ์ƒ์œ„ [[Prototype]] ์ฒด์ธ์— ํ”„๋กœํผํ‹ฐ๊ฐ€ ์žˆ์„ ๋•Œ,
    ์ง์† ํ”„๋กœํผํ‹ฐ๋ฅผ ๋งŒ๋“ ๋‹ค๋ฉด ๊ฐ์ฒด์ง€ํ–ฅ์—์„œ Overriding ์ด๋ผ ๋ถ€๋ฅด๋Š” Shadowing ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค
    โ†’ ์ด ๋‚ด์šฉ์€ ๋‹ค์Œ์— ๊ธฐํšŒ๊ฐ€ ๋˜๋ฉด Prototype ์— ๋Œ€ํ•œ ๊ธ€์„ ์“ฐ๋ฉด์„œ ํ•จ๊ป˜ ๋‹ค๋ฃจ๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค ๐Ÿถ

๐Ÿ‘€ Property - Existence


๊ฐ์ฒด์— ํ”„๋กœํผํ‹ฐ ์กด์žฌ ํ•˜๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค!

  1. in ์—ฐ์‚ฐ์ž ์‚ฌ์šฉ
  2. Object.hasOwnProperty ํ•จ์ˆ˜ ์‚ฌ์šฉ

๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์˜ ์ฐจ์ด์ ์€ in ์—ฐ์‚ฐ์ž๋Š” [[Prototype]] ์ฒด์ธ์„ ๋ชจ๋‘ ์ˆœํšŒํ•˜๋ฉฐ ํ‚ค ์กด์žฌ ์—ฌ๋ถ€๋ฅผ ๊ฒ€์‚ฌํ•˜๊ณ ,
hasOwnProperty ํ•จ์ˆ˜๋Š” ์ง์† ํ”„๋กœํผํ‹ฐ์˜ ์กด์žฌ ์—ฌ๋ถ€๋งŒ์„ ๊ฒ€์‚ฌํ•œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค!

const parent = { parentProp: 'Hey' };
const child = Object.create(parent);
child.childProp = 'Yay!';

// in ์„ ์‚ฌ์šฉํ•˜๋ฉด [[Prototype]] ์—ฐ์‡„์— ์กด์žฌํ•˜๋Š” ํ”„๋กœํผํ‹ฐ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
console.log('parentProp' in child) // true

// hasOwnProperty ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ง์† ํ”„๋กœํผํ‹ฐ์˜ ์กด์žฌ ์—ฌ๋ถ€๋งŒ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค
console.log(child.hasOwnProperty('parentProp')) // false
console.log(child.hasOwnProperty('childProp')) // true

๐Ÿ’Ž Object.seal & Object.freeze


Object.seal, Object.freeze ํ•จ์ˆ˜๋Š” ๋Œ€์ƒ Object ๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์€ ์ƒํƒœ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

  • ๋” ์ด์ƒ ํ™•์žฅ ๋ถˆ๊ฐ€๋Šฅ ํ•˜๊ฒŒ ๋งŒ๋“ฆ(ํ”„๋กœํผํ‹ฐ ์ถ”๊ฐ€๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•œ ์ƒํƒœ),
  • Object.defineProperty ๋กœ Descriptor ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ํ–‰์œ„๋ฅผ ๋ง‰์Œ
  • ํ”„๋กœํผํ‹ฐ ์‚ญ์ œ ๋ถˆ๊ฐ€
function fail () {
  'use strict';

  const obj = { a: 1, b: 2 };
  Object.seal(obj);

  try {
    obj.c = 3;
  } catch (e) {
    // ํ™•์žฅ ๋ถˆ๊ฐ€๋Šฅ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, TypeError!
    console.error(e); // TypeError: Cannot add property c, object is not extensible
  }

  try {
    Object.defineProperty(obj, 'b', { enumerable: false });
  } catch (e) {
    // ์„ค์ • ๋ถˆ๊ฐ€๋Šฅ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, TypeError!
    console.error(e); // TypeError: Cannot redefine property: b
  }

  try {
    delete obj.a;
  } catch (e) {
    // ์‚ญ์ œ ๋ถˆ๊ฐ€๋Šฅ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, TypeError!
    console.error(e); // TypeError: Cannot delete property 'a' of #<Object>
  }
}

fail();

Object.freeze ์˜ ๋™์ž‘์€ Object.seal ์˜ ๋ชจ๋“  ๋™์ž‘์„ ํฌํ•จํ•˜๊ณ 
"ํ”„๋กœํผํ‹ฐ ๊ฐ’" ๋˜ํ•œ ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€ ํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค

ํ•˜์ง€๋งŒ, ์ค‘์ฒฉ๋œ ์ ‘๊ทผ์˜ ๊ฐ’ ๋ณ€๊ฒฝ์€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค
(obj.a = { b: 1 } ์ด๋ผ๋ฉด obj.a ๋Š” ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€ ํ•˜์ง€๋งŒ, obj.a.b ๋Š” ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค)

function freeze () {
  'use strict';

  const catWillBefrozen = {
    name: 'DoonDoony',
    age: 3,
    furColors: ['white', 'coffee'],
    friends: [
      { name: 'TT', age: 3 },
      { name: 'Coco', age: 3 },
    ],
  };

  Object.freeze(catWillBefrozen);

  try {
    catWillBefrozen.name = 'TT'; // ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค
  } catch (e) {
    console.error(e); // TypeError: Cannot assign to read only property 'name' of object '#<Object>'
  }

  catWillBefrozen.furColors.push('pink'); // ํ•˜์ง€๋งŒ ๋ฐฐ์—ด ์กฐ์ž‘์€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค
  console.log(catWillBefrozen.furColors); // ['white', 'coffee', 'pink']

  catWillBefrozen.friends[0].name = 'Momo'; // ๊ฐ์ฒด ์กฐ์ž‘ ๋˜ํ•œ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค
  console.log(catWillBefrozen.friends[0]) // { name: 'Momo', age: 3 }
}

freeze();

๐Ÿ‘ฌ ๊ฐ์ฒด์˜ ๋ณต์‚ฌ


๊ฐ์ฒด์˜ ๋ณต์‚ฌ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์–ธ์–ด๋ฅผ ๋– ๋‚˜ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ ๋ณดํ†ต ๋‘ ๊ฐ€์ง€ ํ˜•ํƒœ๋กœ ์ด๋ฃจ์–ด์ง‘๋‹ˆ๋‹ค
๋ฐ”๋กœ ์–•์€ ๋ณต์‚ฌ(Shallow Copy), ๊นŠ์€ ๋ณต์‚ฌ(Deep Copy) ์ž…๋‹ˆ๋‹ค

์–•์€ ๋ณต์‚ฌ๋Š” Object.assign ๋˜๋Š” Object Spread Operator({ ... }) ๋กœ ์–•์€ ๋ณต์‚ฌ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค
๊นŠ์€ ๋ณต์‚ฌ๋Š” JSON.stringify, JSON.parse๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค

๋‘˜์˜ ์ฐจ์ด์ ์€, ์–•์€ ๋ณต์‚ฌ์˜ ๊ฒฝ์šฐ ํ”„๋กœํผํ‹ฐ๊ฐ€ object(๋ฐฐ์—ด, ํ•จ์ˆ˜ ํฌํ•จ) ๋ผ๋ฉด
๊ฐ’์ด ์•„๋‹Œ ์ฐธ์กฐ๋กœ ๋ณต์‚ฌ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐฐ์—ด, ๊ฐ์ฒด, ํ•จ์ˆ˜์— ๋Œ€ํ•œ ๋ณ€๊ฒฝ์ด ์ƒ๊ธฐ๋ฉด
๋ชจ๋“  ๋ณต์‚ฌ๋œ ๊ฐ์ฒด๊ฐ€ ๊ณต์œ ํ•œ๋‹ค๋Š” ์  ์ž…๋‹ˆ๋‹ค

๊นŠ์€ ๋ณต์‚ฌ ๋ฐฉ๋ฒ•์€, JSON ์ง๋ ฌํ™” ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด๋งŒ์„ ๋ณต์‚ฌํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—
ํ•จ์ˆ˜์™€ ๊ฐ™์€ ํ”„๋กœํผํ‹ฐ๋Š” ๋ณต์‚ฌ๊ฐ€ ๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์  ์ž…๋‹ˆ๋‹ค!

const cat = {
  name: 'DoonDoony',
  age: 3,
  furColors: ['white', 'coffee'],
  speak () {
    console.log(this.name);
  },
};

// Object.assign ์„ ํ•ด ๋ณต์‚ฌ๋ฅผ ์‹œ๋„
const deepCopiedCat = JSON.parse(JSON.stringify(cat));
const newCat = Object.assign({}, cat);
const newCat2 = { ...cat };

function testCopy (origin, new_) {
  'use strict';
  // newCat ์˜ ๊ฐ’ ์กฐ์ž‘
  new_.furColors.push('pink');
  new_.name = 'TT';
  new_.speak.prop = 'function Prop';

  // ์ž˜ ๋ณต์‚ฌ๋˜์–ด ์„œ๋กœ ๋‹ค๋ฅธ ๊ฐ’์„ ์ถœ๋ ฅํ•œ๋‹ค
  console.log(new_.name);
  console.log(origin.name);

  // ์•—! ํ”„๋กœํผํ‹ฐ์˜ ๊ฐ’์ด ๋ฐฐ์—ด์ธ ๊ฒฝ์šฐ ์ฐธ์กฐ ๊ฐ’์„ ๋„˜๊ธฐ๊ธฐ ๋•Œ๋ฌธ์—, ๊ฐ™์ด ๋ณ€๊ฒฝ๋œ๋‹ค
  console.log(new_.furColors);
  console.log(origin.furColors);

  new_.speak(); // 'TT'
  origin.speak(); // 'DoonDoony'

  console.log(new_.speak.prop);
  console.log(origin.speak.prop);
}

testCopy(cat, newCat); // 'TT', 'DoonDoony', ['white', 'coffee', 'pink'], 'function Prop'
testCopy(cat, newCat2); // 'TT', 'DoonDoony', ['white', 'coffee', 'pink', 'pink'], 'function Prop'

try {
  testCopy(cat, deepCopiedCat); // ํ•จ์ˆ˜๊ฐ€ ๋ณต์‚ฌ๋˜์ง€ ์•Š์•„, speak ํ”„๋กœํผํ‹ฐ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค
} catch (e) {
  console.error(e);
}

testCopy ํ•จ์ˆ˜์— newCat2 ๋ฅผ ๋„ฃ์€ ๊ฒฝ์šฐ์—๋Š”
์ด์ „์— 'pink' ๋ฌธ์ž์—ด์ด ์ถ”๊ฐ€๋œ ๋ฐฐ์—ด์ด ๊ทธ๋Œ€๋กœ ๊ณต์œ ๋˜์–ด, 'pink' ๋ฌธ์ž์—ด์ด ๋‘ ๊ฐœ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค

JSON ์ง๋ ฌํ™” ๋ฐฉ๋ฒ•์œผ๋กœ ๊นŠ์€ ๋ณต์‚ฌ๋ฅผ ๊ตฌํ˜„ํ•œ ๊ฒฝ์šฐ์—๋Š”
try - catch ๊ตฌ๋ฌธ์—์„œ ์ฒ˜๋Ÿผ, speak ๋ผ๋Š” ํ•จ์ˆ˜ ํ”„๋กœํผํ‹ฐ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค
(ํ•จ์ˆ˜๋Š” JSON ์ง๋ ฌํ™”๊ฐ€ ๋ถˆ๊ฐ€๋Šฅ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด์ฃ  ๐Ÿ˜Ž)

๐ŸŽ‰ ES6 Object syntax


ES6 ์—์„œ Object ๋ฅผ ์กฐ์ž‘ํ•˜๋Š” ๋ช‡ ๊ฐ€์ง€ ๋ฌธ๋ฒ•์ด ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค

  1. Object Spread Operator
    1. Rest Parameter
  2. Object Destructuring
  3. Property value shorthand
  4. Method definition shorthand

Object Spread Operator

์ฃผ๋กœ Object ์˜ ์–•์€ ๋ณต์‚ฌ (๋ฐ”๋กœ ์œ„์— ๋‚ด์šฉ์ด ์žˆ์–ด์š”!) ์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค

const doondoony = {
  name: 'DoonDoony',
  age: 3,
  favorite: 'Red Ball',
  color: ['white', 'coffee'],
};

// doondoony ๊ฐ์ฒด์˜ ๋ชจ๋“  ๊ฐ’์„ ๋ณต์‚ฌํ•˜๊ณ , ์ค‘๋ณต๋˜๋Š” ํ”„๋กœํผํ‹ฐ๋Š” ์—…๋ฐ์ดํŠธ ํ•ฉ๋‹ˆ๋‹ค
const doondoonyJr = { ...doondoony, name: 'DoonDoon', age: 1 };

console.log(doondoony.age); // 3
console.log(doondoonyJr.age); // 1

Rest Parameter

Object ๋˜ํ•œ ๋‚˜๋จธ์ง€๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” Rest Parameter ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
๋งŒ์•ฝ ํ•ด์ฒด ํ•˜๋ ค๋Š” Object ์— ์—†๋Š” ํ”„๋กœํผํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด undefined ๊ฐ€ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค

const doondoony = {
  name: 'DoonDoony',
  age: 3,
  favorite: 'Red Ball',
  color: ['white', 'coffee'],
};

// ๋‚˜๋จธ์ง€ 
const { name, ...rest } = doondoony;
const { catName, ...rest } = doondoony;

console.log(name) // 'DoonDoony'
console.log(catName) // undefined, ์—†๋Š” ํ”„๋กœํผํ‹ฐ๋ช… ์ž…๋‹ˆ๋‹ค!
console.log(rest) // {age: 3, favorite: 'Red Ball', color: ['white', 'coffee']}

Object Destructuring

Object์—์„œ ๋ช‡๋ช‡์˜ ํ”„๋กœํผํ‹ฐ์— ์ ‘๊ทผํ•  ๋•Œ ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฌธ๋ฒ•์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

const doondoony = {
  name: 'DoonDoony',
  age: 3,
  favorite: 'Red Ball',
  color: ['white', 'coffee'],
};

const { name, age } = doondoony;
console.log(`${name} is ${age} years old`); // DoonDoony is 3 years old

์ด๊ฒƒ์„ ์‘์šฉํ•˜์—ฌ, ํ•จ์ˆ˜์˜ ์ธ์ž๊ฐ€ Object ์ธ ๊ฒฝ์šฐ ์•„๋ž˜์™€ ๊ฐ™์ด ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

const doondoony = {
  name: 'DoonDoony',
  age: 3,
  favorite: 'Red Ball',
  color: ['white', 'coffee'],
};

function howOldAreYou ({ name, age }) {
  console.log(`${name} is ${age} years old`);
}

howOldAreYou(doondoony); // DoonDoony is 3 years old

Property Value Shorhand (ํ”„๋กœํผํ‹ฐ ํ• ๋‹น ๋‹จ์ถ•๊ตฌ๋ฌธ)

Object์˜ ํ”„๋กœํผํ‹ฐ๊ฐ€ ๋ณ€์ˆ˜๋ช…๊ณผ ๋™์ผํ•  ๊ฒฝ์šฐ, ๋ณ€์ˆ˜๋ช… ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

function makeCuteCat (name, age, color) {
  return {
    name, // name: name ์ด๋ผ๊ณ  ์›๋ž˜๋Š” ์ž‘์„ฑํ•ด์•ผ ํ–ˆ์—ˆ์ง€๋งŒ, ๋‹จ์ถ• ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ™์€ ๋™์ž‘์„ ํ•ฉ๋‹ˆ๋‹ค
    age,
    color,
  };
}

const doondoony = makeCuteCat('DoonDoony', 3, ['white', 'coffee']);
console.log(doondoony); // {name: 'DoonDoony', age: 3, color: ['white', 'coffee']}

Method definition shorthand(๋ฉ”์„œ๋“œ ์ •์˜ ์ถ•์•ฝ ๋ฌธ๋ฒ•)

Object ๋‚ด๋ถ€์— ํ•จ์ˆ˜๋ฅผ ๊ฐ’์œผ๋กœ ๊ฐ€์ง„ ํ”„๋กœํผํ‹ฐ (= ๋ฉ”์„œ๋“œ) ๋ฅผ ๋งŒ๋“ค ๊ฒฝ์šฐ
์•„๋ž˜์™€ ๊ฐ™์€ ์ถ•์•ฝ ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

    const doondoony = {
      name: 'DoonDoony',
      age: 3,
        // speak: function() {} ๊ณผ ๊ฐ™์€ ๋ฐฉ์‹์—์„œ ์ถ•์•ฝ ํ•ด์„œ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
      speak () {
        console.log(this.name);
      },
    };

Object ๋ฉ”์„œ๋“œ ์ถ•์•ฝ๋ฌธ๋ฒ•๊ณผ class ๋ฉ”์„œ๋“œ๋ฅผ ์ •์˜๋Š” ์กฐ๊ธˆ์˜ ์ฐจ์ด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค
๋ฐ”๋กœ ์ฝค๋งˆ(,) ๋กœ ๊ตฌ๋ถ„ํ•ด์•ผ ํ•œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค (ํ”„๋กœํผํ‹ฐ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋‹น์—ฐํ•˜์ง€๋งŒ ๐Ÿ˜…)

const doondoony = {
  name: 'DoonDoony',
  speak () {
    console.log(this.name);
  },
  //  ๋‹ค์Œ ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด ์ฝค๋งˆ(,) ๋ฅผ ์‚ฌ์šฉํ•ด ๊ตฌ๋ถ„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค
  age: 3,
};

const DoonDoony = class {
  constructor (name, age) {
    this.name = name;
    this.age = age;
  }
  // constructor ์™€ speak ์‚ฌ์ด์— ์ฝค๋งˆ(,) ์—†์Œ
  speak () {
    console.log(this.name)
  }
};

doondoony.speak(); // 'DoonDoony'
new DoonDoony('DoonDoony', 3).speak(); // 'DoonDoony'

๋งˆ์น˜๋ฉฐ


๋‘์„œ์—†๊ณ  ์—„์ฒญ๋‚˜๊ฒŒ ๊ธด ๊ธ€ ์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค ๐Ÿ‘
ํ•ญ์ƒ ์ž˜๋ชป๋œ ์ •๋ณด๋Š” ๋Œ“๊ธ€ ๋‚จ๊ฒจ ์ฃผ์‹œ๋ฉด ์ˆ˜์ •ํ† ๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ธด ๊ธ€์„ ๋งˆ์น˜๋ฉฐ ์ด ๊ธ€์„ ์“ฐ๊ฒŒ๋œ ๊ณ„๊ธฐ์— ๋Œ€ํ•ด ๋‹ค์‹œ ์งš์–ด๋ณด์•˜์Šต๋‹ˆ๋‹ค

  1. ์›์‹œ ๊ฐ’์„ ์žฌ์™ธํ•œ ๋ชจ๋“  ๊ฒƒ์€ object ์ธ๋ฐ, ๋„ˆ๋ฌด ์ž˜ ๋ชจ๋ฅด๊ณ  ์“ฐ๊ณ  ์žˆ๋Š”๊ฑด ์•„๋‹๊นŒ
  2. (๋‹ค์Œ์— ๊ธ€์„ ์“ธ ์ง€๋„ ๋ชจ๋ฅด๋Š”) new ๋ฅผ ๋ฐฐ์ œํ•˜๊ณ 
    ์ง„์งœ ๊ฐ์ฒด ์ง€ํ–ฅ์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด object์— ๋Œ€ํ•ด ๋” ์ž˜ ์•Œ์•„์•ผ ํ•˜์ง€ ์•Š์„๊นŒ
    ์ด๋Ÿฐ ์ด์œ ๋กœ ๊ธ€์„ ์ ๋‹ค ๋ณด๋‹ˆ ์—ฌ๊ธฐ๊นŒ์ง€ ๊ธด ๊ธ€์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค

์ •๋ฆฌ๋ฅผ ํ•˜๊ณ  ๋‚˜๋‹ˆ๊นŒ( ๊ทธ๋ ‡๋‹ค๊ณ  Object ๋ผ๋Š” ๊ฒƒ์„ ์™„์ „ํžˆ ์ดํ•ดํ•œ ๊ฒƒ์€ ์•„๋‹ˆ์ง€๋งŒ )
๊ทธ ์ „์— ๋ชจ๋ฅด๋˜ ์„œ์ˆ ์ž๋‚˜, ๊ฐ’์˜ ์ฐธ์กฐ ๋ฐฉ๋ฒ•
๊ทธ๋ฆฌ๊ณ  ๋‹ค์–‘ํ•œ Object ์˜ ๋ฉ”์„œ๋“œ ๋“ฑ์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์•Œ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค

์‚ฌ์‹ค Object ๋Œ€๋ถ€๋ถ„์˜ ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•ด ๋‹ค๋ฃจ์—ˆ์ง€๋งŒ
Object.create ์— ๋Œ€ํ•ด์„œ๋Š” ์ตœ ์ƒ๋‹จ์— ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ์ ์–ด ๋†“๊ธฐ๋งŒ ํ•˜๊ณ  ์•„๋ฌด๋Ÿฐ ์„ค๋ช…์ด ์—†์—ˆ๋Š”๋ฐ์š”

์ดํ›„์—๋Š” Object.create ์™€ ๊ทธ๊ฒƒ์„ ๋‘˜๋Ÿฌ์‹ผ [[Prototype]] ์— ๋Œ€ํ•ด ์„ค๋ช…ํ•˜๊ณ 
๊ถ๊ทน์ ์œผ๋กœ ์ œ๊ฐ€ ๊ณต๋ถ€ํ•˜๊ณ  ์‹ถ์—ˆ๋˜ ์ฃผ์ œ์ธ ๊ฐ์ฒด๋ฅผ ํด๋ž˜์Šค์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•˜๊ธฐ ์— ๋Œ€ํ•ด ๋‚จ๊ธฐ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค!

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!