1. How to create the Object πŸ‘©β€πŸ’»

객체 생성 과정을 톡해 JavaScript Object 의 Prototype 을 μ΄ν•΄ν•˜κ³  ES6 에 μΆ”κ°€λœ ES6 Class Syntax 와 λΉ„κ΅ν•΄λ³΄μž.

1. Object (Constructor Function)

JavaScript 의 κ°μ²΄λŠ” μ΅œμƒμœ„μ— ObjectλΌλŠ” 객체λ₯Ό 두고, κ·Έ 객체λ₯Ό μ—°κ²°ν•΄ ν•˜μœ„ ꡬ쑰둜 써내렀간닀. λ”°λΌμ„œ JavaScript λ₯Ό Prototype Language 라 λΆ€λ₯΄λ©°, μ΄λ“€κ°„μ˜ 관계λ₯Ό Prototype Chain 이라 ν•œλ‹€.

그리고 μ’€ 더 λͺ…ν™•νžˆ μ΄μ•ΌκΈ°ν•˜λ©΄ JavaScript μ—λŠ” Interfaces 뿐 μ•„λ‹ˆλΌ Classes μ—­μ‹œ μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ”λ‹€. ES6 이전 λ¬Έλ²•μ—μ„œλŠ” μƒμ„±μžλ₯Ό λ‹€μŒκ³Ό 같이 λ§Œλ“€ 수 μžˆμ—ˆλ‹€.

function Person(name, age) {
  this.name = name
  this.age = age

  this.greet = function () {
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`)
  }
}

ν•˜μ§€λ§Œ μœ„ 방법은 λ©”λͺ¨λ¦¬λ₯Ό κ³Όλ„ν•˜κ²Œ μ‚¬μš©ν•  수 μžˆλŠ” λ¬Έμ œμ μ„ κ°–λŠ”λ‹€. Class λ₯Ό μ‚¬μš©ν•˜κ² λ‹€λŠ” 것은 μž¬μ‚¬μš©μ„ ν•˜κΈ° μœ„ν•¨μ΄λ‹€. 그런데 μœ„μ™€ 같이 μƒμ„±μž ν•¨μˆ˜μ— λ©”μ„œλ“œκ°€ ν¬ν•¨λ˜λŠ” 경우, 2개의 instance λ₯Ό μƒμ„±ν•˜λ©΄ 각각 instance κ°€ λ©”μ„œλ“œ μ—­μ‹œ 각각 μƒμ„±ν•œλ‹€. 일반적으둜 Class 객체가 λ©”μ„œλ“œλ₯Ό κ³΅μœ ν•˜μ§€λ§Œ Properties 와 Closures 만 μžμ‹ μ˜ instance 에 μ €μž₯ν•˜λŠ” 것과 달리 이 방법은 λͺ¨λ“  것을 각각 μžμ‹ μ˜ instance 에 μ €μž₯ν•˜λŠ” λ¬Έμ œμ μ„ κ°–λŠ” 것이닀.

λ”°λΌμ„œ 이 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ λ‹€μŒκ³Ό 같이 Constructor 만 μ •μ˜ν•œ ν›„ Methods λ₯Ό Prototype Chain 을 μ΄μš©ν•΄ μΆ”κ°€ν•˜λŠ” κ²ƒμœΌλ‘œ λ³€κ²½ν•  수 μžˆλ‹€.

function Person(name, age) {
  this.name = name
  this.age = age
}

Person.prototype.greet = function () {
  console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`)
}

μ΄λ ‡κ²Œ μž‘μ„±ν•˜λ©΄ μ—¬λŸ¬ 개의 instance λ₯Ό μƒμ„±ν•˜λ”λΌλ„ Prototype 을 톡해 λ©”μ„œλ“œλ₯Ό κ³΅μœ ν•  수 μžˆλ‹€.

2. Object (ES6 Class Syntax)

μœ„μ—μ„œ μž‘μ„±ν•œ μƒμ„±μž μ½”λ“œλ₯Ό ES6 μ—μ„œ μΆ”κ°€λœ Class Syntax 둜 λ³€κ²½ν•˜λ©΄ λ‹€μŒκ³Ό κ°™λ‹€.

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }

  greet() {
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`)
  }
}
const person = new Person('홍길동', 25)
person.greet()  // Hello, my name is 홍길동 and I'm 25 years old.

ES6 의 Class Syntax λŠ” λ‚΄λΆ€μ μœΌλ‘œλŠ” 1. μƒμ„±μž ν•¨μˆ˜λ₯Ό μƒμ„±ν•˜κ³ , 2. Prototype 에 λ©”μ„œλ“œλ₯Ό λ“±λ‘ν•˜λŠ” λΆ„λ¦¬λœ λ‘œμ§μ„ μžλ™ν™” μ‹œμΌœμ£ΌλŠ” Syntactic Sugar 에 κ°€κΉŒμš΄ 문법이닀.

단, 타 μ–Έμ–΄μ—μ„œ μƒκ°ν•˜λŠ” Class μ™€λŠ” ꡬ쑰적으둜, κΈ°λŠ₯적으둜 λ§Žμ€ 차이λ₯Ό κ°–λŠ”λ‹€. μ΄λŠ” JavaScript 의 문법적 νŠΉμ„±μ΄λ‹ˆ 이λ₯Ό λͺ…ν™•νžˆ μ΄ν•΄ν•˜κ³  μ‚¬μš©ν•˜λŠ” 것이 μ€‘μš”ν•˜λ‹€.

3. Object Literal

Class λ₯Ό μ‚¬μš©ν•˜λŠ” μ΄μœ λŠ” instance 생성을 μž¬μ‚¬μš© ν•˜κΈ° μœ„ν•¨μ΄λ‹€. 그런데 instance 생성을 μž¬μ‚¬μš© ν•  ν•„μš”κ°€ μ—†λŠ” ν•˜λ“œμ½”λ”©λœ κ°μ²΄λΌλ˜κ°€ instance 생성을 ν•œ 번만 ν•˜λŠ” Singleton 객체와 같은 것듀은 μ†μ‰½κ²Œ Object Literal 을 μ΄μš©ν•΄ 생성할 수 μžˆλ‹€.

  • ν•˜λ“œμ½”λ”© 된 객체
const Person = {
  name: '홍길동',
  age: 25,
  greet: function () {
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`)
  }
}
  • Singleton 객체
const Person = {
  name: '',
  age: 0,

  init(name: string, age: number) {
    this.name = name
    this.age = age
  },

  greet() {
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`)
  }
}
Person.init('홍길동', 25)
console.log(Person.greet())  // Hello, my name is 홍길동 and I'm 25 years old.

JavaScript 의 Class λŠ” Object 의 Prototype 객체에 μƒμ„±μž ν•¨μˆ˜λ₯Ό λ‹€λ₯Έ μ–Έμ–΄μ˜ Class 문법과 μœ μ‚¬ν•˜κ²Œ λ§Œλ“€μ–΄ μ‚¬μš©ν•˜κΈ° μ‰½κ²Œ ν•΄μ£ΌλŠ” Syntactic Sugar 에 가깝기 λ•Œλ¬Έμ— μ΄λŸ°μ‹μ˜ 생성이 κ°€λŠ₯ν•˜λ‹€.

Object Literal 방식은 Singleton 객체λ₯Ό μƒμ„±ν•œλ‹€. λ‹€λ₯Έ μ–Έμ–΄μ—μ„œ Singleton Class λ₯Ό κ΅¬ν˜„ν•˜κΈ° μœ„ν•΄ μ‚¬μš©ν•˜λŠ” private initializer와 Instance λ₯Ό μƒμ„±ν•˜λŠ” λ©”μ„œλ“œ, Synchronous μ²˜λ¦¬μ™€ 같은 것듀 없이 μ†μ‰½κ²Œ Singleton 을 κ΅¬ν˜„ν•  수 μžˆλ‹€.

4. Closures (Functional Programming)

λ§ˆμ§€λ§‰μœΌλ‘œ μ™„μ „νžˆ ν•¨μˆ˜ν˜•μœΌλ‘œ Closures λ₯Ό μ‚¬μš©ν•œ 객체 생성 방법이닀.

function Person(name: string, age: number) {
  let _name = name
  let _age = age

  function getName(): string {
    return _name
  }

  function setName(name: string) {
    _name = name
  }

  function getAge(): number {
    return _age
  }

  function setAge(age: number) {
    _age = age
  }
  function greet() {
    console.log(`Hello, my name is ${_name} and I'm ${_age} years old.`)
  }

  return {
    getName, setName, getAge, setAge, greet
  }
}
const person = Person('홍길동', 25)
person.greet()  // Hello, my name is 홍길동 and I'm 25 years old.

5. Arrow Functions are not equal to Functions

ES6 μ—μ„œ Arrow Functions κ°€ μ†Œκ°œλœ μ΄ν›„λ‘œ 기쑴의 Functions 문법을 λΉ λ₯΄κ²Œ λŒ€μ²΄ν•˜κ³  μžˆλ‹€. Arrow Functions κ°€ κ°–λŠ” μž₯점은 λ‹€μŒκ³Ό κ°™λ‹€.

  1. Lambda Expression 기반으둜 가독성이 λ›°μ–΄λ‚˜λ‹€.
  2. ν•¨μˆ˜κ°€ 호좜될 λ•Œ λ™μ μœΌλ‘œ this κ°€ binding λ˜λŠ” 일반 ν•¨μˆ˜μ™€ 달리 μ„ μ–Έν•  λ•Œ μƒμœ„ Scope 의 this λ₯Ό κΈ°μ–΅ν•œλ‹€. 이λ₯Ό Lexical Scope라 ν•œλ‹€.

λ¬Έμ œλŠ” μ € 2번째 동적인 this μ—μ„œ 정적인 this λ₯Ό μ‚¬μš©ν•  수 μžˆμ§€λ§Œ 였히렀 μƒμœ„ Scope λ₯Ό κ°€λ¦¬ν‚€λŠ” 문제둜 인해 λ©”μ„œλ“œμ— μž‘μ„±ν•  λ•ŒλŠ” 잘λͺ»λœ λŒ€μƒμ„ κ°€λ¦¬ν‚€κ²Œ λœλ‹€λŠ” λ¬Έμ œκ°€ μžˆλ‹€. λ‹€λ₯Έ μ–Έμ–΄μ—μ„œλŠ” 객체 λ‚΄μ—μ„œ λ©”μ„œλ“œκ°€ this λ₯Ό 가리킀면 Method body context λ₯Ό κΈ°μ€€μœΌλ‘œ λ°”κΉ₯μͺ½μ— ν•΄λ‹Ήν•˜λŠ” 자기 μžμ‹ μ΄ μ†ν•œ 객체λ₯Ό this 둜 κ°€λ¦¬ν‚€μ§€λ§Œ JavaScript 의 Arrow Functions μ—μ„œ this λŠ” Method body context κ°€ μ•„λ‹Œ Method λ₯Ό κΈ°μ€€μœΌλ‘œ λ°”κΉ₯μͺ½μ„ this 둜 가리킨닀.

const something2 = {
  whoAreYou: 'something2',
  aTraditionalFunction: function () {
    console.log(this.whoAreYou)
  },
  aArrowFunction: () => console.log(this.whoAreYou)
}

something2.aTraditionalFunction() // something2
something2.aArrowFunction()       // undefined

something2 의 this κ°€ κ°€λ¦¬ν‚€λŠ” λŒ€μƒμ΄ λˆ„κ΅¬κΈΈλž˜ undefined κ°€ 좜λ ₯될까?

const something2 = {
  whoAreYou: 'something2',
  aTraditionalFunction: function () {
    console.log(this)
  },
  aArrowFunction: () => console.log(this)
}

something2.aTraditionalFunction() // {whoAreYou: 'something2', aTraditionalFunction: Ζ’, aArrowFunction: Ζ’}
something2.aArrowFunction()       // Window {window: Window, self: Window, document: document, name: '', location: Location, …}

Arrow Functions 의 this λŠ” something2 객체의 λ°”κΉ₯μͺ½μ„ κ°€λ¦¬ν‚€κ³ μžˆλ‹€. Window 객체에 β€˜whoAreYou’ κ°€ μ •μ˜λœ 적 μ‘°μ°¨ μ—†μœΌλ―€λ‘œ undefined κ°€ 좜λ ₯λ˜λŠ” 것이닀.


그러면 λ‹€μŒμ˜ κ²½μš°λŠ” μ–΄λ–¨κΉŒ?

const something1 = {
  whoAreYou: 'something1',
  something2: {
    whoAreYou: 'something2',
    aTraditionalFunction: function () {
      console.log(this.whoAreYou)
    },
    aArrowFunction: () => console.log(this.whoAreYou)
  }
}

something1.something2.aTraditionalFunction()  // something2
something1.something2.aArrowFunction()        // undefined

something2 λ₯Ό something1 내뢀에 μ •μ˜ν–ˆλ‹€. μ—¬κΈ°μ„œ this λŠ” something1 을 가리킀고, β€˜something1’ 이 좜λ ₯될 κ²ƒμœΌλ‘œ μ˜ˆμƒν•  수 μžˆμ§€λ§Œ μ—¬μ „νžˆ undefined κ°€ 좜λ ₯λœλ‹€.

const something1 = {
  whoAreYou: 'something1',
  something2: {
    whoAreYou: 'something2',
    aTraditionalFunction: function () {
      console.log(this)
    },
    aArrowFunction: () => console.log(this)
  }
}

something1.something2.aTraditionalFunction()  // {whoAreYou: 'something2', aTraditionalFunction: Ζ’, aArrowFunction: Ζ’}
something1.something2.aArrowFunction()        // Window {window: Window, self: Window, document: document, name: '', location: Location, …}

μ—¬μ „νžˆ Window 객체λ₯Ό κ°€λ¦¬ν‚¨λ‹€β€ΌοΈπŸ€―πŸ€―

μ™œ 이런 ν˜„μƒμ΄ μΌμ–΄λ‚˜λŠ” κ²ƒμΌκΉŒ? μ„ μ–Έν•  λ•Œ μƒμœ„ Scope 의 this λ₯Ό κΈ°μ–΅ν•œλ‹€κ³  ν–ˆλ‹€. μ•„λž˜ 객체가 μƒμ„±λ˜λŠ” 과정을 ν’€μ–΄μ„œ 보면 λ‹€μŒκ³Ό κ°™λ‹€.

const something1 = {
  whoAreYou: 'something1',
}

const something2 = {
  whoAreYou: 'something2',
  aTraditionalFunction: function () {
    console.log(this.whoAreYou)
  },
  aArrowFunction: () => console.log(this.whoAreYou)
}

something1.something2 = something2

즉, something2 Instance κ°€ μƒμ„±λ˜λŠ” μ‹œμ μ— this λŠ” Window λ₯Ό 가리킨닀. Instance κ°€ μƒμ„±λœ ν›„ something1 의 something2 Property 에 ν• λ‹Ήλ˜κΈ° λ•Œλ¬Έμ— ν•΄λ‹Ή Arrow Functions κ°€ μ„ μ–Έλ˜κ³  μ •μ˜λ˜λŠ” μ‹œμ μ— this λŠ” Window 객체고 이λ₯Ό κΈ°μ–΅ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

즉, λ©”μ„œλ“œλ₯Ό μž‘μ„±ν•  λ•Œ Arrow Functions λ₯Ό μ‚¬μš©ν•˜λŠ” 것은 μžμ‹ μ΄ μ†ν•œ Object 객체λ₯Ό this 둜 가리킀지 μ•ŠκΈ° λ•Œλ¬Έμ— 일반적인 객체의 λ©”μ„œλ“œλ‘œμ¨μ˜ κΈ°λŠ₯을 μˆ˜ν–‰ν•  수 μ—†λ‹€.

Arrow Functions λŠ” 단지 Syntax 만 λ‹€λ₯Έ 것이 μ•„λ‹Œ 기쑴의 ν•¨μˆ˜μ™€λŠ” λ‹€λ₯Έ κΈ°λŠ₯을 κ°–λŠ” μƒˆλ‘œμš΄ ν•¨μˆ˜ λΌλŠ” 점을 μžŠμ–΄μ„œλŠ” μ•ˆ λœλ‹€.


2. Prototype Chain πŸ‘©β€πŸ’»

μ΄λ²ˆμ—λŠ” μœ„μ—μ„œ μ‚΄νŽ΄λ³Έ Prototype 이 μ‹€μ œλ‘œ μ–΄λ–»κ²Œ Chaining λ˜λŠ”μ§€ 확인해본닀.

1. Constructor Function

function Person(name, age) {
  this.name = name
  this.age = age

  this.greet = function () {
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`)
  }
}
console.dir(Person)

Prototype Chain 1

  • constructor: f Person(name, age)
  • Prototype Chain: self -> Base Object

2. ES6 Class Syntax

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }

  greet() {
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`)
  }
}
console.dir(Person.prototype)

Prototype Chain 2

  • constructor: class Person
  • Prototype Chain: self -> Base Object

3. Object Literal

const Person = {
  name: '홍길동',
  age: 25,
  greet() {
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`)
  }
}
console.dir(Person.__proto__)

Prototype Chain 3

  • constructor: none
  • Prototype Chain: Base Object

3. Inheritance πŸ‘©β€πŸ’»

1. Constructor Function

1 ) Superclass

function Person(name, age) {
  this.name = name
  this.age = age

  this.greet = function () {
    console.log(`Hello, my name is ${this.name}, I'm ${this.age} years old.`)
  }
}


2 ) Subclass

function Student(name, age, grade) {
  Person.call(this, name, age)
  this.grade = grade

  this.study = function () {
    console.log(`I'm studying in grade ${this.grade}.`)
  }
}
const student = new Student("Jane", 20, 2)
student.greet() // Hello, my name is Jane, I'm 20 years old.
student.study() // I'm studying in grade 2.

μƒμ†μœΌλ‘œ 잘 μž‘λ™ν•˜λŠ” 것을 λ³Ό 수 μžˆλ‹€.

student Instance -> Student Object -> Person Object -> Base Object

2. ES6 Class Syntax

1 ) Superclass

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }

  greet() {
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`)
  }
}


2 ) Subclass

class Student extends Person {
  constructor(name, age, grade) {
    super(name, age)
    this.grade = grade
  }

  study() {
    console.log(`I'm studying in grade ${this.grade}.`)
  }
}
const student1 = new Student("Jane", 20, 2)
student1.greet() // "Hello, my name is Jane and I'm 20 years old."
student1.study() // "I'm studying in grade 2."

Parent κ°€ ES6 Class Syntax λ₯Ό μ‚¬μš©ν–ˆλ‹€λ©΄, Children μ—­μ‹œ ES6 Class Syntax λ₯Ό μ‚¬μš©ν•΄μ•Όν•œλ‹€!!
πŸ‘‰πŸ» 이 경우 Children μ—μ„œ Parent 의 Person.call(this, name, age)λ₯Ό ν˜ΈμΆœν•  수 μ—†λ‹€.

단, λ°˜λŒ€μ˜ 경우 Parent κ°€ Constructor Function 을 μ‚¬μš©ν–ˆλ”λΌλ„ Children 은 ES6 Class Syntax λ₯Ό μ‚¬μš©ν•΄ μƒμ†ν•˜λŠ” 것이 κ°€λŠ₯ν•˜λ‹€.


4. Two Phase Initialization πŸ‘©β€πŸ’»

1. Constructor Function

1 ) Super 의 Constructor 호좜 μ‹œμ μ΄ μ€‘μš”ν•˜μ§€ μ•Šμ€ 경우

function Person(name, age) {
  this.name = name
  this.age = age

  this.greet = function () {
    console.log(`Hello, my name is ${this.name}, I'm ${this.age} years old.`)
  }
}

Parent 객체의 값이 Constructor λ₯Ό 톡해 μ΄ˆκΈ°ν™”κ°€ κ°€λŠ₯ν•˜λ‹€λ©΄, Children 객체의 Constructor κ°€ Super 의 Constructor λ₯Ό ν˜ΈμΆœν•˜λŠ” μ‹œμ μ΄ 값을 μ €μž₯ν•œ ν›„λ˜ 호좜 ν›„ μ €μž₯을 ν•˜λ˜ Prototype 에 μ˜ν•΄ μ•„λ¬΄λŸ° 영ν–₯을 λ―ΈμΉ˜μ§€ μ•ŠλŠ”λ‹€.

function Student(name, age, grade) {
  this.name = name
  this.age = age
  this.grade = grade
  Person.call(this, name, age)

  this.study = function () {
    console.log(`I'm studying in grade ${this.grade}.`)
  }
}

const student = new Student("Jane", 20, 2)

console.log(student)    // Student {name: 'Jane', age: 20, grade: 2, greet: Ζ’, study: Ζ’}
function Student(name, age, grade) {
  Person.call(this, name, age)
  this.name = name
  this.age = age
  this.grade = grade

  this.study = function () {
    console.log(`I'm studying in grade ${this.grade}.`)
  }
}

const student = new Student("Jane", 20, 2)

console.log(student)  // Student {name: 'Jane', age: 20, grade: 2, greet: Ζ’, study: Ζ’}


2 ) Super 의 Constructor 호좜 μ‹œμ μ΄ μ€‘μš”ν•œ 경우

function Person() {
  this.name = '홍길동'
  this.age = 25

  this.greet = function () {
    console.log(`Hello, my name is ${this.name}, I'm ${this.age} years old.`)
  }
}

ν•˜μ§€λ§Œ μœ„μ™€ 같이 Parent 객체의 값이 Constructor λ₯Ό 톡해 μ΄ˆκΈ°ν™”κ°€ κ°€λŠ₯ν•œ 값이 아닐 경우 Super 의 Constructor λ₯Ό ν˜ΈμΆœν•˜λŠ” μ‹œμ μ΄ μ€‘μš”ν•΄μ§„λ‹€.

function Student(name, age, grade) {
  this.name = name
  this.age = age
  this.grade = grade
  Person.call(this, name, age)

  this.study = function () {
    console.log(`I'm studying in grade ${this.grade}.`)
  }
}

const student = new Student("Jane", 20, 2)

console.log(student)  // Student {name: '홍길동', age: 25, grade: 2, greet: Ζ’, study: Ζ’}

Jane이 μ•„λ‹Œ 홍길동이 μƒμ„±λ˜λŠ” 것을 λ³Ό 수 μžˆλ‹€!!

function Student(name, age, grade) {
  Person.call(this, name, age)
  this.name = name
  this.age = age
  this.grade = grade

  this.study = function () {
    console.log(`I'm studying in grade ${this.grade}.`)
  }
}

const student = new Student("Jane", 20, 2)

console.log(student)  // Student {name: 'Jane', age: 20, grade: 2, greet: Ζ’, study: Ζ’}


3 Recommend Way

JavaScript μ—μ„œ Subclass 의 Constructor λŠ” 항상 Superclass 의 Constructor λ₯Ό μƒμ„±ν•œ ν›„ 값을 μˆ˜μ • λ˜λŠ” μ •μ˜ν•˜λ„λ‘ ν•œλ‹€!!

function Student(name, age, grade) {
  Person.call(this, name, age)
  this.name = name
  this.age = age
  this.grade = grade

  this.study = function () {
    console.log(`I'm studying in grade ${this.grade}.`)
  }
}

const student = new Student("Jane", 20, 2)

console.log(student)  // Student {name: 'Jane', age: 20, grade: 2, greet: Ζ’, study: Ζ’}

2. ES6 Class Syntax

단, ES6 Class Syntax λ₯Ό μ‚¬μš©ν–ˆμ„ κ²½μš°λŠ” μ’€ 더 μ—„κ²©ν•˜κ²Œ κ·œμΉ™μ΄ μ μš©λœλ‹€.

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }

  greet() {
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`)
  }
}


λ”°λΌμ„œ λ‹€μŒμ€ μ—λŸ¬κ°€ λ°œμƒν•œλ‹€.

class Student extends Person {
  constructor(name, age, grade) {
    this.name = name    // error, 'this' is not allowed before superclass constructor invocation.
    this.age = age      // error, 'this' is not allowed before superclass constructor invocation. 
    this.grade = grade  // error, 'this' is not allowed before superclass constructor invocation.
    super(name, age)
  }

  study() {
    console.log(`I'm studying in grade ${this.grade}.`)
  }
}

λ”°λΌμ„œ λ‹€μŒκ³Ό 같이 μ •μ˜ν•΄μ•Όλ§Œ μ—λŸ¬κ°€ λ°œμƒν•˜μ§€ μ•ŠλŠ”λ‹€.

class Student extends Person {
  constructor(name, age, grade) {
    super(name, age)
    this.name = name
    this.age = age
    this.grade = grade
  }

  study() {
    console.log(`I'm studying in grade ${this.grade}.`)
  }
}

Swift 의 Two Phase Initialization 와 λ‹€λ₯Έ 점은 Subclass μ—μ„œ μ •μ˜ν•˜λŠ” Properties μ—­μ‹œ Superclass 의 Constructor λ₯Ό 호좜 ν•œ 이후 Phase 2μ—μ„œ ν•¨κ»˜ μ •μ˜ν•΄μ•Όν•œλ‹€λŠ” 것이닀.

class Student extends Person {
  constructor(name, age, grade) {
    this.grade = grade
    super(name, age)
    this.name = name
    this.age = age
  }

  study() {
    console.log(`I'm studying in grade ${this.grade}.`)
  }
}

λ”°λΌμ„œ μœ„μ™€ 같은 문법 μ—­μ‹œ 잘λͺ»λ˜μ—ˆλ‹€.

class Student extends Person {
  constructor(name, age, grade) {
    super(name, age)
    this.name = name
    this.age = age
    this.grade = grade
  }

  study() {
    console.log(`I'm studying in grade ${this.grade}.`)
  }
}

λ°˜λ“œμ‹œ Super 의 Constructor λ₯Ό ν˜ΈμΆœν•œ 이후 μ΄λ£¨μ–΄μ Έμ•Όν•œλ‹€!!


5. ES6 Class Private πŸ‘©β€πŸ’»

1. ES6 Class Support Private Properties and Methods

class Counter {
  #count = 0

  next = function () {
    return ++this.#count
  }

  #reset = function () {
    this.#count = 0
  }
}
const counter = new Counter()

console.log(counter.next())   // 1
console.log(counter.next())   // 2
console.log(counter.next())   // 3
console.log(counter.count)    // undefined
console.log(counter.reset())  // caught TypeError: counter.reset is not a function

Private 으둜 μ„ μ–Έλœ Properties 와 Methods λŠ” μ™ΈλΆ€μ—μ„œ μ ‘κ·Όν•  수 μ—†λ‹€.

2. ES6 Class Private cannot be hidden

λ¬Έμ œλŠ” ν•΄λ‹Ή μ΄λ¦„μœΌλ‘œ 접근을 λ§‰κ² λ‹€λŠ” 것이지 μ •λ§λ‘œ 은닉화λ₯Ό ν•˜λŠ” 것은 μ•„λ‹ˆλ‹€.

console.log(counter.count)  // undefined
console.log(counter.#count) // caught SyntaxError: Private field '#count' must be declared in an enclosing class
counter.reset()   // caught TypeError: counter.reset is not a function
counter.#reset()  // caught SyntaxError: Private field '#reset' must be declared in an enclosing class

은닉화가 μž‘λ™ν•˜λŠ” 것 κ°™μ§€λ§Œ κ°œλ°œμžκ°€ μž‘μ„±ν•˜λŠ” μ½”λ“œ μž‘μ„± λ°©μ‹μ—μ„œ private 으둜 μž‘λ™ν•œλ‹€λŠ” 것이지 μ •λ§λ‘œ 객체의 Properties μžμ²΄κ°€ hiding λ˜λŠ” 것은 μ•„λ‹ˆλ‹€.

console.log(counter)  // Counter {#count: 3, #reset: Ζ’, next: Ζ’}

Closures λ₯Ό μ΄μš©ν•˜λ©΄ κ°€λŠ₯ν•˜μ§€ μ•Šμ„κΉŒ?

객체λ₯Ό 좜λ ₯ν–ˆμ„ λ•Œ private 이 κ΅¬ν˜„λ  수 μžˆλŠ” μœ μΌν•œ 방법이닀. 단, Closures λ₯Ό μ‚¬μš©ν•œ 객체 생성을 μ‚¬μš©ν•  경우 맀번 μƒˆ 객체가 μƒμ„±λ˜λŠ”λ° GCκ°€ μ œλŒ€λ‘œ μž‘λ™ν•˜μ§€ μ•Šμ•„ Memory Leak 이 생길 수 μžˆλ‹€κ³  ν•œλ‹€.

JavaScript 의 built-in objects 쀑 Map, Set, WeakMap, WeakSet 등을 μ΄μš©ν•˜λŠ” 방법을 μ†Œκ°œν•˜λŠ” λΈ”λ‘œκ·Έλ„ μ‘΄μž¬ν–ˆμ§€λ§Œ λ‹¨μˆœνžˆ dot syntax둜 접근이 μ•ˆ λ˜λŠ” 것 뿐이닀. μ›λž˜ set, get λ©”μ„œλ“œλ‘œ κ΄€λ¦¬ν•˜λŠ” 객체듀이라 일 뿐 private 이 μ•„λ‹ˆλ‹€.


6. Rect Examples πŸ‘©β€πŸ’»

1. ES6 Class Getter/Setter

struct Size {
    var width = 0.0, height = 0.0
}

struct Point {
    var x = 0.0, y = 0.0
}

struct Rect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            Point(x: origin.x + (size.width / 2),
                  y: origin.y + (size.height / 2))
        }
        set {
            origin.x = newValue.x - (size.width / 2)
            origin.y = newValue.y - (size.height / 2)
        }
    }
}

μœ„ μ½”λ“œλ₯Ό JavaScript 의 ES6 Class λ₯Ό μ΄μš©ν•΄ κ΅¬ν˜„ν•΄λ³΄μž.

class Point {
  constructor(x = 0, y = 0) {
    this.x = x
    this.y = y
  }
}

class Size {
  constructor(width = 0, height = 0) {
    this.width = width
    this.height = height
  }
}

class Rect {
  constructor(origin = new Point(), size = new Size()) {
    this.origin = origin
    this.size = size
  }

  get center() {
    const centerX = this.origin.x + (this.size.width / 2)
    const centerY = this.origin.y + (this.size.height / 2)
    return new Point(centerX, centerY)
  }

  set center(newCenter) {
    this.origin.x = newCenter.x - (this.size.width / 2)
    this.origin.y = newCenter.y - (this.size.height / 2)
  }
}
const square = new Rect(new Point(), new Size(10, 10))
console.log(square.origin)                    // Point {x: 0, y: 0}
console.log(square.center)                    // Point {x: 5, y: 5}

square.center = new Point(17.5, 17.5)
console.log('square.origin', square.origin)   // Point {x: 12.5, y: 12.5}
console.log('square.center', square.center)   // Point {x: 17.5, y: 17.5}

2. Object.defineProperty()

Object.defineProperty()둜 λ°”κΏ”λ³΄μž.

class Rect {
  constructor(origin = new Point(), size = new Size()) {
    this.origin = origin
    this.size = size

    Object.defineProperty(this, 'center', {
      get: function () {
        const centerX = this.origin.x + (this.size.width / 2)
        const centerY = this.origin.y + (this.size.height / 2)
        return new Point(centerX, centerY)
      },
      set: function (newCenter) {
        this.origin.x = newCenter.x - (this.size.width / 2)
        this.origin.y = newCenter.y - (this.size.height / 2)
      }
    });
  }
}
const square = new Rect(new Point(), new Size(10, 10))
console.log(square.origin)                    // Point {x: 0, y: 0}
console.log(square.center)                    // Point {x: 5, y: 5}

square.center = new Point(17.5, 17.5)
console.log('square.origin', square.origin)   // Point {x: 12.5, y: 12.5}
console.log('square.center', square.center)   // Point {x: 17.5, y: 17.5}

3. Use Object.defineProperty() likes Swift extensions

class Rect {
  constructor(origin = new Point(), size = new Size()) {
    this.origin = origin
    this.size = size
  }
}

Object.defineProperty(Rect.prototype, 'center', {
  get: function () {
    const centerX = this.origin.x + (this.size.width / 2)
    const centerY = this.origin.y + (this.size.height / 2)
    return new Point(centerX, centerY)
  },
  set: function (newCenter) {
    this.origin.x = newCenter.x - (this.size.width / 2)
    this.origin.y = newCenter.y - (this.size.height / 2)
  }
})

Object.defineProperty()λ₯Ό Class μ„ μ–Έκ³Ό λΆ„λ¦¬μ‹œμΌœ Swift 의 extensions λ₯Ό μ‚¬μš©ν•˜λ“― 뢄리해 λ‹€λ£° 수 μžˆλ‹€.

const square = new Rect(new Point(), new Size(10, 10))
console.log(square.origin)                    // Point {x: 0, y: 0}
console.log(square.center)                    // Point {x: 5, y: 5}

square.center = new Point(17.5, 17.5)
console.log('square.origin', square.origin)   // Point {x: 12.5, y: 12.5}
console.log('square.center', square.center)   // Point {x: 17.5, y: 17.5}

4. Use Object.prototype likes Swift extensions

μœ„ 3번이 κ°€λŠ₯ν•˜λ‹€λŠ” 것을 λ³΄μ•˜μœΌλ‹ˆ prototype 을 직접 μ΄μš©ν•˜λŠ” 것도 κ°€λŠ₯해보인닀. κ²°λ‘ λΆ€ν„° λ§ν•˜μžλ©΄ 이 방법은 μ‹€νŒ¨ν•œλ‹€.

prototype 을 μ΄μš©ν•œ ν™•μž₯은 λ‹¨μˆœν•œ Properties λ‚˜ Methods μ •λ„λ§Œ ν™•μž₯이 κ°€λŠ₯ν•˜λ‹€. Getter/Setter와 같은 것듀을 λ“±λ‘ν•˜λ €λ©΄ λ°˜λ“œμ‹œ Object.defineProperty()λ₯Ό μ‚¬μš©ν•΄μ•Όν•œλ‹€.

class Rect {
  constructor(origin = new Point(), size = new Size()) {
    this.origin = origin
    this.size = size
  }
}

Rect.prototype.center = {
  get: function () {
    const centerX = this.origin.x + (this.size.width / 2)
    const centerY = this.origin.y + (this.size.height / 2)
    return new Point(centerX, centerY)
  },
  set: function (newCenter) {
    this.origin.x = newCenter.x - (this.size.width / 2)
    this.origin.y = newCenter.y - (this.size.height / 2)
  }
}
const square = new Rect(new Point(), new Size(10, 10))
console.log(square.origin)                    // Point {x: 0, y: 0}
console.log(square.center)                    // {get: Ζ’, set: Ζ’}

square.center = new Point(17.5, 17.5)
console.log('square.origin', square.origin)   // Point {x: 0, y: 0}
console.log('square.center', square.center)   // Point {x: 17.5, y: 17.5}

Object.prototype을 μ΄μš©ν•œ 방법은 Getter/Setter와 같은 것듀을 μ²˜λ¦¬ν•  수 μ—†μ–΄ μœ„μ™€ 같이 μ˜ˆμƒκ³Ό 달리 잘λͺ»λœ κ²°κ³Όλ₯Ό λ„μΆœν•˜κ²Œλœλ‹€.

5. Proxy and Reflect

μœ„ λ‘œμ§μ„ Proxy 와 Reflect λ₯Ό μ‚¬μš©ν•˜λŠ” λ°©λ²•μœΌλ‘œ λ³€κ²½ν•΄λ³΄μž.

class Rect {
  constructor(origin = new Point(), size = new Size()) {
    this.origin = origin
    this.size = size
  }
}

const squareProxyHandler = {
  get(target, property, receiver) {
    switch (property) {
      case 'center':
        const centerY = target.origin.y + (target.size.height / 2)
        const centerX = target.origin.x + (target.size.width / 2)
        return new Point(centerX, centerY)
      default:
        // return Reflect.get(target, property, receiver)
        return Reflect.get(...arguments)
    }
  },
  set(target, property, newValue, receiver) {
    switch (property) {
      case 'center':
        target.origin.x = newValue.x - (target.size.width / 2)
        target.origin.y = newValue.y - (target.size.height / 2)
        break
      default:
        return Reflect.set(...arguments)
    }
  }
}
const square = new Rect(new Point(), new Size(10, 10))
const squareProxy = new Proxy(square, squareProxyHandler)
console.log('squareProxy.origin', squareProxy.origin)   // Point {x: 0, y: 0}
console.log('squareProxy.center', squareProxy.center)   // Point {x: 5, y: 5}

squareProxy.center = new Point(17.5, 17.5)
console.log('squareProxy.origin', squareProxy.origin)   // Point {x: 12.5, y: 12.5}
console.log('squareProxy.center', squareProxy.center)   // Point {x: 17.5, y: 17.5}
  • μž₯점: Proxy λ₯Ό μ‚¬μš©ν•˜λ©΄ λ™μΌν•œ ν˜•νƒœμ˜ μ„œλ‘œ λ‹€λ₯Έ μΌ€μ΄μŠ€λ₯Ό ν•˜λ‚˜μ˜ μ •μ˜λ‘œ μž¬μ‚¬μš©ν•  수 μžˆλ‹€
  • 단점: Reflection 은 λΉ„μš©μ΄ 많이 λ“œλŠ” λΉ„μ¦ˆλ‹ˆμŠ€ 둜직이고 μ½”λ“œλŸ‰μ΄ λŠ˜μ–΄λ‚œλ‹€.