class inheritance - Lee-hyuna/33-js-concepts-kr GitHub Wiki

클래슀 상속

원문: Class inheritance

클래슀 상속은 ν•œ ν΄λž˜μŠ€κ°€ λ‹€λ₯Έ 클래슀λ₯Ό ν™•μž₯ν•˜λŠ” 방법이닀.

λ”°λΌμ„œ κΈ°μ‘΄ κΈ°λŠ₯ μœ„μ— μƒˆλ‘œμš΄ κΈ°λŠ₯을 λ§Œλ“€ 수 μžˆλ‹€.

"extends"

Animal ν΄λž˜μŠ€κ°€ μžˆλ‹€κ³  κ°€μ • ν•΄ 보자

class Animal {
  constructor(name) {
    this.speed = 0;
    this.name = name;
  }
  run(speed) {
    this.speed += speed;
    alert(`${this.name} runs with speed ${this.speed}.`);
  }
  stop() {
    this.speed = 0;
    alert(`${this.name} stands still.`);
  }
}

let animal = new Animal("My animal");

animal 객체와 Animal 클래슀λ₯Ό 그림으둜 ν‘œν˜„ν•˜λ©΄ μ•„λž˜μ™€ κ°™λ‹€.

/resource/yongkwan/31/01.svg

이 Animal 클래슀둜 Rabbit 클래슀λ₯Ό λ§Œλ“€μ–΄ 보자.

ν† λΌλŠ” 동물이기 λ•Œλ¬Έμ— Rabbit ν΄λž˜μŠ€λŠ” Animal에 κΈ°λ°˜μ„ 두며, Animal 클래슀의 λ©”μ„œλ“œμ— μ ‘ν•  수 μžˆλ‹€. λ”°λΌμ„œ ν† λΌλŠ” "일반적인" 동물이 ν•  수 μžˆλŠ” 일을 ν•  수 μžˆλ‹€.

클래슀λ₯Ό ν™•μž₯ν•˜λŠ” ꡬ문은 λ‹€μŒκ³Ό κ°™λ‹€.: class Child extends Parent.

Animal을 상속받은 class Rabbit을 λ§Œλ“€μ–΄ 보자 :

class Rabbit extends Animal {
  hide() {
    alert(`${this.name} hides!`);
  }
}

let rabbit = new Rabbit("White Rabbit");

rabbit.run(5); // White Rabbit runs with speed 5.
rabbit.hide(); // White Rabbit hides!

Rabbit클래슀둜 λ§Œλ“  κ°μ²΄λŠ” rabbit.hide()와 같은 Rabbit λ©”μ„œλ“œμ™€ rabbit.run()와 같은 Animal λ©”μ„œλ“œ λ‘˜λ‹€ 접근이 κ°€λŠ₯ν•˜λ‹€.

λ‚΄λΆ€μ μœΌλ‘œ extends ν‚€μ›Œλ“œλŠ” κΈ°μ‘΄ ν”„λ‘œν† νƒ€μž… 기법을 μ‚¬μš©ν•˜μ—¬ λ™μž‘ν•œλ‹€. extends λŠ” Rabbit.prototype.[Prototype](/Lee-hyuna/33-js-concepts-kr/wiki/Prototype)λ₯Ό Animal.prototype둜 μ„€μ •ν•œλ‹€. λ”°λΌμ„œ Rabbit.prototype에 μ°ΎλŠ” λ©”μ†Œλ“œκ°€ μ—†μœΌλ©΄ JavaScriptλŠ” Animal.prototypeμ—μ„œ λ©”μ†Œλ“œλ₯Ό μ°ΎλŠ”λ‹€.

/resource/yongkwan/31/02.svg

예λ₯Ό λ“€μ–΄, rabbit.run λ©”μ„œλ“œλ₯Ό μ°ΎκΈ° μœ„ν•΄ 엔진은 λ‚΄λΆ€μ μœΌλ‘œ μ•„λž˜μ™€ 같이 λ™μž‘ν•œλ‹€.

  1. rabbit객체(run이 μ—†λŠ”) 확인.
  2. ν”„λ‘œν† νƒ€μž…μΈ Rabbit.prototype(hideλŠ” μžˆμ§€λ§Œ run이 μ—†λŠ”) 확인
  3. ν”„λ‘œν† νƒ€μž…μΈ Animal.prototype( extends으둜 인해) 확인. λ§ˆμΉ¨λ‚΄ run λ©”μ†Œλ“œλ₯Ό μ°ΎλŠ”λ‹€ .

Native prototypes μž₯μ—μ„œ κΈ°μ–΅ν•  수 μžˆλ“―μ΄, JavaScript μžμ²΄λŠ” λ‚΄μž₯ 객체에 ν”„λ‘œν†  νƒ€μž… 상속을 μ‚¬μš©ν•©λ‹ˆλ‹€. 예λ₯Ό λ“€λ©΄ Date.prototype.[Prototype](/Lee-hyuna/33-js-concepts-kr/wiki/Prototype)λŠ” Object.prototype이닀. κ·Έλ ‡κΈ° λ•Œλ¬Έμ— λ‚ μ§œκ°€ 일반 객체 λ©”μ†Œλ“œμ— μ ‘κ·Όν•  수 μžˆλ‹€.

extends λ’€μ—” μ–΄λ–€ ν‘œν˜„μ΄ 와도 상관 μ—†λ‹€ 클래슀 ꡬ문은 extends 뒀에 μ–΄λ–€ ν‘œν˜„μ΄μ™€λ„ λœλ‹€.

예λ₯Ό λ“€μ–΄, λΆ€λͺ¨ 클래슀λ₯Ό μƒμ„±ν•˜λŠ” ν•¨μˆ˜ 호좜 :

function f(phrase) {
return class {
   sayHi() { alert(phrase) }
 }
}

class User extends f("Hello") {}

new User().sayHi(); // Hello

μ—¬κΈ°μ„œ class UserλŠ” f("Hello") κ²°κ³Όλ‘œλΆ€ν„° λ₯Ό 상속 받은 것 이닀.

상황에 따라 클래슀λ₯Ό μƒμ„±ν•˜λŠ” ν•¨μˆ˜μ‚¬μš©ν•˜μ—¬ μƒμ†ν•œλ‹€λ©΄ κ³ κΈ‰ ν”„λ‘œκ·Έλž˜λ° νŒ¨ν„΄μ— 유용 ν•  수 μžˆλ‹€.

λ©”μ†Œλ“œ μ˜€λ²„λΌμ΄λ”©(μž¬μ •μ˜)

λ©”μ†Œλ“œλ₯Ό μž¬μ •μ˜ 방법을 μ•Œμ•„λ³΄μž. 기본적으둜 class Rabbit에 μ—†λŠ” λͺ¨λ“  λ©”μ†Œλ“œλŠ” class Animalμ—μ„œ "μžˆλŠ” κ·ΈλŒ€λ‘œ" κ°€μ Έμ˜¨λ‹€.

κ·ΈλŸ¬λ‚˜ μ•„λž˜ μ˜ˆμ œμ—μ„œ 보듯이 stop()와 같이 Rabbit 내뢀에 자체 λ©”μ†Œλ“œλ₯Ό μ§€μ •ν•˜λ©΄ λŒ€μ‹  μ‚¬μš©λœλ‹€.

class Rabbit extends Animal {
  stop() {
    // ...now this will be used for rabbit.stop()
    // instead of stop() from class Animal
  }
}

일반적으둜 μ˜€λ²„λΌμ΄λ”©μ€ λΆ€λͺ¨ λ©”μ†Œλ“œλ₯Ό μ™„μ „νžˆ λŒ€μ²΄ν•œλ‹€λŠ” 것 보단, κ·Έ μœ„μ— κΈ°λŠ₯을 μ‘°μ • λ˜λŠ” ν™•μž₯ν• λ•Œ μ‚¬μš©ν•œλ‹€. 우리의 λ©”μ„œλ“œμ—μ„œ λ­”κ°€λ₯Ό ν•˜μ§€λ§Œ, κ·Έ μ „ν›„λ‚˜ κ·Έ κ³Όμ •μ—μ„œ λΆ€λͺ¨ λ©”μ„œλ“œλ₯Ό λΆ€λ₯Έλ‹€.

ν΄λž˜μŠ€λŠ” 이λ₯Όμœ„ν•œ "super" ν‚€μ›Œλ“œλ₯Ό μ œκ³΅ν•œλ‹€.

  • super.method(...)λŠ” λΆ€λͺ¨ λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•œλ‹€.
  • super(...)λŠ” λΆ€λͺ¨ μƒμ„±μžλ₯Ό ν˜ΈμΆœν•œλ‹€. (μƒμ„±μž λ‚΄λΆ€μ—μ„œλ§Œ)

예λ₯Ό λ“€μ–΄, 토끼가 λ©ˆμΆ”μ—ˆμ„ λ•Œ μžλ™ 숨기기λ₯Ό μ„€μ •ν•΄λ³΄μž.

class Animal {

  constructor(name) {
    this.speed = 0;
    this.name = name;
  }

  run(speed) {
    this.speed += speed;
    alert(`${this.name} runs with speed ${this.speed}.`);
  }

  stop() {
    this.speed = 0;
    alert(`${this.name} stands still.`);
  }

}

class Rabbit extends Animal {
  hide() {
    alert(`${this.name} hides!`);
  }

  stop() {
    super.stop(); // call parent stop
    this.hide(); // and then hide
  }
}

let rabbit = new Rabbit("White Rabbit");

rabbit.run(5); // White Rabbit runs with speed 5.
rabbit.stop(); // White Rabbit stands still. White rabbit hides!

이제 Rabbit은 λΆ€λͺ¨μ˜ λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜λŠ” super.stop()을 가진 stopλ©”μ„œλ“œκ°€ μžˆλ‹€.

ν™”μ‚΄ν‘œ ν•¨μˆ˜λŠ” superκ°€ μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ”λ‹€.

ν™”μ‚΄ν‘œ ν•¨μˆ˜ μž₯μ—μ„œ μ–ΈκΈ‰ν–ˆλ“―μ΄ ν™”μ‚΄ν‘œ ν•¨μˆ˜μ—λŠ” superκ°€ μ—†λ‹€.

super에 μ ‘κ·Όν•˜λ €ν•˜λ©΄ μ™ΈλΆ€ ν•¨μˆ˜μ—μ„œ κ°€μ Έμ˜¨λ‹€. 예λ₯Ό λ“€μ–΄ :

class Rabbit extends Animal {
  stop() {
    setTimeout(() => super.stop(), 1000); // call parent stop after 1sec
  }
}

ν™”μ‚΄ν‘œ ν•¨μˆ˜μ•ˆ superλŠ” stop() ν•¨μˆ˜μ•ˆμ— super와 λ™μΌν•˜λ‹€. κ·Έλž˜μ„œ μ˜λ„λŒ€λ‘œ μž‘λ™ν•œλ‹€. 여기에 일반적인 ν•¨μˆ˜λ₯Ό μ§€μ •ν•˜λ©΄ 였λ₯˜κ°€ λ°œμƒν•œλ‹€.

// Unexpected super
setTimeout(function() { super.stop() }, 1000);

μƒμ„±μž μ˜€λ²„λΌμ΄λ”©(μž¬μ •μ˜)

μƒμ„±μžλ₯Ό μ˜€λ²„λΌμ΄λ”©ν•˜λŠ” 것은 μ•½κ°„ κΉŒλ‹€λ‘­λ‹€.

μ§€κΈˆκΉŒμ§€λŠ” Rabbit에 constructorκ°€ μ—†μ—ˆλ‹€.

μŠ€νŽ™ 에 λ”°λ₯΄λ©΄ , ν΄λž˜μŠ€κ°€ λ‹€λ₯Έ 클래슀λ₯Ό ν™•μž₯ν•˜κ³  constructorμ—†λŠ” 경우 λ‹€μŒκ³Ό 같은 "빈" constructor이 μƒμ„±λœλ‹€.

class Rabbit extends Animal {
  // generated for extending classes without own constructors
  constructor(...args) {
    super(...args);
  }
}

μœ„μ—μ„œ λ³Ό 수 μžˆλ“―μ΄ 기본적으둜 λͺ¨λ“  인수λ₯Ό μ „λ‹¬ν•˜λ©΄μ„œ λΆ€λͺ¨μ˜ constructor ν˜ΈμΆœν•œλ‹€. μžμ‹ μ˜ μƒμ„±μžλ₯Ό μž‘μ„±ν•˜μ§€ μ•ŠμœΌλ©΄ μœ„μ²˜λŸΌ λ™μž‘ν•œλ‹€.

μ΄μ œμ— Rabbit에 μ‚¬μš©μž μ •μ˜ μƒμ„±μžλ₯Ό μΆ”κ°€ν•΄λ³΄μž. nameκ³Ό earLength 인자둜 λ°›λŠ” μƒμ„±μžλ₯Ό λ§Œλ“€ 것이닀.

class Animal {
  constructor(name) {
    this.speed = 0;
    this.name = name;
  }
  // ...
}

class Rabbit extends Animal {

  constructor(name, earLength) {
    this.speed = 0;
    this.name = name;
    this.earLength = earLength;
  }

  // ...
}

// Doesn't work!
let rabbit = new Rabbit("White Rabbit", 10); // Error: this is not defined.

μœΌμ•…! 였λ₯˜κ°€ λ°œμƒν–ˆλ‹€. 토끼λ₯Ό λ§Œλ“œλŠ”λ° μ‹€νŒ¨ν–ˆλ‹€. 무엇이 잘λͺ» λμ„κΉŒ?

닡은 상속 클래슀의 μƒμ„±μžκ°€ thisλ₯Ό μ‚¬μš©ν•˜κΈ°μ „μ— .super(...) ν˜ΈμΆœν•΄μ•Όλ§Œ ν•œλ‹€λŠ” 것이닀.

κ·ΈλŸ¬λ‚˜ μ™œ κ·Έλž˜μ•Όλ§Œν• κΉŒ?

μžμ„Έν•œ 진행 상황을 보면 이해가 κ°€λŠ₯ν•˜λ‹€.

JavaScriptμ—λŠ” 상속 클래슀의 μƒμ„±μž ν•¨μˆ˜λ§Œμ˜ νŠΉμ§•μ΄ μžˆλ‹€. 상속 클래슀의 μƒμ„±μž ν•¨μˆ˜λŠ” νŠΉμˆ˜ν•œ λ‚΄λΆ€ μ†μ„±μœΌλ‘œ λ ˆμ΄λΈ”μ΄ μ§€μ •λœλ‹€. [ConstructorKind](/Lee-hyuna/33-js-concepts-kr/wiki/ConstructorKind):"derived".

차이점은 λ‹€μŒκ³Ό κ°™λ‹€.

  • 일반 μƒμ„±μžκ°€ 싀행될 λ•Œ, 빈 객체λ₯Ό λ§Œλ“€μ–΄ this에 ν• λ‹Ήν•œλ‹€.
  • κ·ΈλŸ¬λ‚˜ 상속 클래슀의 μƒμ„±μžκ°€ 싀행될 λ•ŒλŠ”, 이 μž‘μ—…μ„ μˆ˜ν–‰ν•˜μ§€ μ•ŠλŠ”λ‹€. λΆ€λͺ¨ μƒμ„±μžκ°€ 이 μž‘μ—…μ„ μˆ˜ν–‰ ν•  κ²ƒμœΌλ‘œ κΈ°λŒ€ν•œλ‹€.

λ”°λΌμ„œ 상속 ν΄λž˜μŠ€μ— μƒμ„±μžλ₯Ό λ§Œλ“€μ—ˆλ‹€λ©΄ superλ₯Ό ν˜ΈμΆœν•΄μ•Όν•œλ‹€. 그렇지 μ•ŠμœΌλ©΄ this λŒ€ν•œ 객체가 μƒμ„±λ˜μ§€ μ•ŠλŠ”λ‹€. 그리고 μ—λŸ¬κ°€ λ°œμƒν•œλ‹€.

예λ₯Ό λ“€μ–΄ Rabbit μƒμ„±μžκ°€ λ™μž‘ν•˜λ €λ©΄ μ•„λž˜μ²˜λŸΌ thisλ₯Ό μ‚¬μš©ν•˜κΈ° 전에, super()λ₯Ό ν˜ΈμΆœν•΄ν•œλ‹€.

lass Animal {

  constructor(name) {
    this.speed = 0;
    this.name = name;
  }

  // ...
}

class Rabbit extends Animal {

  constructor(name, earLength) {
    super(name);
    this.earLength = earLength;
  }

  // ...
}

// now fine
let rabbit = new Rabbit("White Rabbit", 10);
alert(rabbit.name); // White Rabbit
alert(rabbit.earLength); // 10

Super: internals, HomeObject

μ°Έκ³ 

이 νŠœν† λ¦¬μ–Όμ„ 처음 μ½λŠ” 경우 이 μ„Ήμ…˜μ„ κ±΄λ„ˆ λ›Έ 수 μžˆλ‹€. super와 상속 λ‚΄λΆ€ λ©”μ»€λ‹ˆμ¦˜μ— κ΄€ν•œ 것이닀.

superλ₯Ό μ’€ 더 깊게 νŒŒμ•…ν•΄ 보자. 이 κ³Όμ •μ—μ„œ ν₯미둜운 것듀을 보게 될 것이닀.

superκ°€ 기술적으둜 μ–΄λ–»κ²Œ μž‘λ™ν•΄μ•Όν•˜λŠ”μ§€ μŠ€μŠ€λ‘œμ—κ²Œ λ¬Όμ–΄ 보자. 객체 λ©”μ†Œλ“œκ°€ 싀행될 λ•Œ, 객체 λ©”μ†Œλ“œλŠ” ν˜„μž¬ 객체λ₯Ό this둜 가진닀. super.method() ν˜ΈμΆœν•˜λ©΄ 엔진은 methodλ₯Ό 이 ν˜„μž¬ 객체의 ν”„λ‘œν† νƒ€μž…μ—μ„œ κ°€μ Έμ™€μ•Όν•œλ‹€. κ·ΈλŸ¬λ‚˜ μ–΄λ–»κ²Œ κ°€μ Έμ˜¬κΉŒ?

λ‹¨μˆœ ν•΄ 보일 수 μžˆμ§€λ§Œ 그렇지 μ•Šλ‹€. 엔진은 ν˜„μž¬ 객체( this)λ₯Ό μ•Œκ³ μžˆμœΌλ―€λ‘œ, λΆ€λͺ¨λ₯Ό methodλ₯Ό this.__proto__.method둜써 κ°€μ Έμ˜¬ 수 μžˆμ„κ²ƒλ§Œ κ°™λ‹€. λΆˆν–‰νžˆλ„ μ΄λŸ¬ν•œ "μˆœμ§„ν•œ" μ†”λ£¨μ…˜μ€ μž‘λ™ν•˜μ§€ μ•ŠλŠ”λ‹€.

λ¬Έμ œκ°€ λ¬΄μ—‡μΌκΉŒ? ν΄λž˜μŠ€κ°€ μ—†μœΌλ©΄ λ‹¨μˆœμ„±μ„ μœ„ν•΄ 일반 객체λ₯Ό μ‚¬μš©ν•œλ‹€.

μ„ΈλΆ€ 사항을 μ•ŒκΈ°λ₯Ό μ›ν•˜μ§€ μ•ŠμœΌλ©΄ 이 뢀뢄을 κ±΄λ„ˆλ›°κ³  ν•˜μœ„ μ„Ήμ…˜μΈ [HomeObject](/Lee-hyuna/33-js-concepts-kr/wiki/HomeObject)으둜 이동해도 λœλ‹€ . λ¬Έμ œλ˜μ§„ μ•ŠλŠ”λ‹€. ν•˜μ§€λ§Œ 깊이 μ΄ν•΄ν•˜λŠ” 데 관심이 μžˆλ‹€λ©΄ 계속 μ½μ–΄λ³΄μž.

μ•„λž˜ μ˜ˆμ—μ„œ rabbit.__proto__ = animalλ₯Ό 보라. 이제 rabbit.eat()μ•ˆμ—μ„œ this.__proto__λ₯Ό μ‚¬μš©ν•΄μ„œ animal.eat()λ₯Ό ν˜ΈμΆœν•΄λ³΄μž.

let animal = {
  name: "Animal",
  eat() {
    alert(`${this.name} eats.`);
  }
};

let rabbit = {
  __proto__: animal, // rabbit.__proto__ = animal
  name: "Rabbit",
  eat() {
    // that's how super.eat() could presumably work
    this.__proto__.eat.call(this); // (*)
  }
};

rabbit.eat(); // Rabbit eats.

(*) 라인을 보면 ν”„λ‘œν† νƒ€μž…( animal)μ—μ„œ eat을 κ°€μ Έμ™€μ„œ ν˜„μž¬ 객체의 μ»¨ν…μŠ€νŠΈμ—μ„œ ν˜ΈμΆœν•œλ‹€. μ—¬κΈ°μ„œ μ€‘μš”ν•œ 건 .call(this)이닀. μ™œλƒν•˜λ©΄ this.__proto__.eat()으둜만 μ‹€ν–‰ν•œλ‹€λ©΄, ν˜„μž¬ 객체의 μ»¨ν…μŠ€νŠΈκ°€ μ•„λ‹Œ ν”„λ‘œν† νƒ€μž…μ˜ μ»¨ν…μŠ€νŠΈ μ•ˆμ— μžˆλŠ” λΆ€λͺ¨μ˜ eat μ‹€ν–‰ν•  것이닀.

μœ„μ˜ μ½”λ“œλŠ” μ‹€μ œλ‘œ μ˜λ„ν•œλŒ€λ‘œ alert이 λ™μž‘ν•œλ‹€.

이제 체인에 객체λ₯Ό ν•˜λ‚˜ 더 μΆ”κ°€ν•΄λ³΄μž. μ–΄λ–»κ²Œ λ§κ°€μ§€λŠ”μ§€ λ³Ό 것이닀.:

let animal = {
  name: "Animal",
  eat() {
    alert(`${this.name} eats.`);
  }
};

let rabbit = {
  __proto__: animal,
  eat() {
    // ...bounce around rabbit-style and call parent (animal) method
    this.__proto__.eat.call(this); // (*)
  }
};

let longEar = {
  __proto__: rabbit,
  eat() {
    // ...do something with long ears and call parent (rabbit) method
    this.__proto__.eat.call(this); // (**)
  }
};

longEar.eat(); // Error: Maximum call stack size exceeded

μ½”λ“œκ°€ 더 이상 λ™μž‘ν•˜μ§€ μ•ŠλŠ”λ‹€! longEar.eat()λ₯Ό ν˜ΈμΆœν•˜λ©΄ 였λ₯˜κ°€ λ°œμƒν•œλ‹€.

그닀지 λͺ…λ°±ν•˜μ§€λŠ” μ•Šμ§€λ§Œ, longEar.eat()ν˜ΈμΆœμ„ 따라가닀 보면 이유λ₯Ό μ•Œ 수 μžˆλ‹€. (*)및 (**)의 두 λΌμΈμ—μ„œ this의 값은 은 ν˜„μž¬ 였브젝트 (longEar)이닀. λͺ¨λ“  객체 λ©”μ†Œλ“œλŠ” ν˜„μž¬ 객체λ₯Ό this둜 가진닀.

λ”°λΌμ„œ (*)κ³Ό (**) 두 라인의 κ°’ this.__proto__λŠ” 값은 rabbitκ°’μœΌλ‘œ μ •ν™•νžˆ λ™μΌν•˜λ‹€. λ”°λΌμ„œ λ‘˜ λ‹€ rabbit.eatλ₯Ό λŠμž„ 없이 ν˜ΈμΆœν•œλ‹€.

무슨일이 μΌμ–΄λ‚˜λŠ”μ§€ μ•„λž˜ κ·Έλ¦Όμ—μ„œ μ„€λͺ…ν•œλ‹€.

/resource/yongkwan/31/03.svg

  1. longEar.eat()μ•ˆμ—μ„œ, (**)라인은 rabbit.eat호좜 ν•˜λŠ”λ°, this값은 longEar이닀.
// inside longEar.eat() we have this = longEar
this.__proto__.eat.call(this) // (**)
// becomes
longEar.__proto__.eat.call(this)
// that is
rabbit.eat.call(this);
  1. 그런 λ‹€μŒ rabbit.eat의 (*)λΌμΈμ—μ„œ , 더 높은 체인에 μžˆλŠ” ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜κ³  μ‹Άμ§€λ§Œ this=longEar이닀 . λ”°λΌμ„œ this.__proto__.eatλŠ” λ‹€μ‹œ rabbit.eatκ°€ λœλ‹€.
// inside rabbit.eat() we also have this = longEar
this.__proto__.eat.call(this) // (*)
// becomes
longEar.__proto__.eat.call(this)
// or (again)
rabbit.eat.call(this);
  1. λ”°λΌμ„œ rabbit.eat ν˜ΈμΆœμ€ 더 이상 올라갈 수 μ—†κΈ° λ•Œλ¬Έμ— ν˜ΈμΆœμ€ 자기 μžμ‹ μ„ λŠμž„ 없이 ν˜ΈμΆœν•œλ‹€.

이 λ¬Έμ œλŠ” thisλ§Œμ„ μ‚¬μš©ν•΄μ„  ν•΄κ²°ν•  수 μ—†λ‹€.

[HomeObject](/Lee-hyuna/33-js-concepts-kr/wiki/HomeObject)

해결책을 μ œκ³΅ν•˜κΈ° μœ„ν•΄ JavaScriptλŠ” ν•¨μˆ˜μ— λŒ€ν•œ λ‚΄λΆ€ 속성인 [HomeObject](/Lee-hyuna/33-js-concepts-kr/wiki/HomeObject)을 μΆ”κ°€ν•œλ‹€.

ν•¨μˆ˜κ°€ 클래슀 λ˜λŠ” 객체 λ©”μ„œλ“œλ‘œ μ§€μ •λ˜λ©΄ [HomeObject](/Lee-hyuna/33-js-concepts-kr/wiki/HomeObject)속성이 ν•΄λ‹Ή κ°μ²΄κ°€λ©λ‹ˆλ‹€.

그런 λ‹€μŒ superκ°€ [HomeObject](/Lee-hyuna/33-js-concepts-kr/wiki/HomeObject) 속성을 μ‚¬μš©ν•˜μ—¬ μƒμœ„ ν”„λ‘œν† νƒ€μž…μ„ μ—°κ²°ν•œλ‹€.

λ¨Όμ € 일반 객체둜 μž‘λ™ 방식을 μ‚΄νŽ΄λ³΄μž.

let animal = {
  name: "Animal",
  eat() {         // animal.eat.[HomeObject](/Lee-hyuna/33-js-concepts-kr/wiki/HomeObject) == animal
    alert(`${this.name} eats.`);
  }
};

let rabbit = {
  __proto__: animal,
  name: "Rabbit",
  eat() {         // rabbit.eat.[HomeObject](/Lee-hyuna/33-js-concepts-kr/wiki/HomeObject) == rabbit
    super.eat();
  }
};

let longEar = {
  __proto__: rabbit,
  name: "Long Ear",
  eat() {         // longEar.eat.[HomeObject](/Lee-hyuna/33-js-concepts-kr/wiki/HomeObject) == longEar
    super.eat();
  }
};

// works correctly
longEar.eat();  // Long Ear eats.

[HomeObject](/Lee-hyuna/33-js-concepts-kr/wiki/HomeObject)으둜 인해 μ˜λ„ ν•œλŒ€λ‘œ λ™μž‘ν•œλ‹€. longEar.eat와 같은 λ©”μ†Œλ“œλŠ” [HomeObject](/Lee-hyuna/33-js-concepts-kr/wiki/HomeObject)을 μ•ŒκΈ°λ•Œλ¬Έμ— thisλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³ λ„ λΆ€λͺ¨ λ©”μ†Œλ“œλ₯Ό ν”„λ‘œν† νƒ€μž…μ—μ„œ κ°€μ Έμ˜¨λ‹€.

λ©”μ„œλ“œλŠ” μžμœ κ°€ μ•„λ‹ˆλ‹€

μš°λ¦¬κ°€ 이전에 μ•Œκ³  μžˆλ“―μ΄, 일반적으둜 ν•¨μˆ˜λŠ” "자유"이며 JavaScript의 객체에 λ°”μΈλ”©λ˜μ§€ μ•ŠλŠ”λ‹€. λ”°λΌμ„œ 객체간에 λ³΅μ‚¬ν•˜μ—¬ λ‹€λ₯Έ 객체와 this와 ν•¨κ»˜ 호좜 ν•  수 μžˆλ‹€.

κ·ΈλŸ¬λ‚˜ [HomeObject](/Lee-hyuna/33-js-concepts-kr/wiki/HomeObject)의 쑴재 μžμ²΄κ°€ κ·Έ 원칙을 μœ„λ°˜ν•œλ‹€. [HomeObject](/Lee-hyuna/33-js-concepts-kr/wiki/HomeObject)λ³€κ²½ν•  수 μ—†μœΌλ―€λ‘œ 바인딩은 μ˜μ›νžˆ μ§€μ†λœλ‹€.

[HomeObject](/Lee-hyuna/33-js-concepts-kr/wiki/HomeObject)κ°€ μ‚¬μš©λ˜λŠ” μœ μΌν•œ μž₯μ†Œ λŠ” super이닀. λ”°λΌμ„œ, superλ₯Ό μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” λ©”μ†Œλ“œμΌ κ²½μš°μ—” 자유둭고 볡사가 κ°€λŠ₯ν•˜λ‹€. .κ·ΈλŸ¬λ‚˜ superλ₯Ό μ‚¬μš©ν•œλ‹€λ©΄ 상황이 잘λͺ» 될 수 μžˆλ‹€.

superμ‚¬μš©ν•œ λ©”μ†Œλ“œλ₯Ό λ³΅μ‚¬ν•˜μ—¬ 잘λͺ»λœ κ²½μš°λŠ” λ‹€μŒκ³Ό κ°™λ‹€.

let animal = {
  sayHi() {
    console.log(`I'm an animal`);
  }
};

// rabbit inherits from animal
let rabbit = {
  __proto__: animal,
  sayHi() {
    super.sayHi();
  }
};

let plant = {
  sayHi() {
    console.log("I'm a plant");
  }
};

// tree inherits from plant
let tree = {
  __proto__: plant,
  sayHi: rabbit.sayHi // (*)
};

tree.sayHi();  // I'm an animal (?!?)

tree.sayHi()λ₯Ό ν˜ΈμΆœν•˜λ©΄ "I’m an animal"이 좜λ ₯λœλ‹€. λΆ„λͺ…νžˆ 잘λͺ»λλ‹€..

μ΄μœ λŠ” κ°„λ‹¨ν•˜λ‹€.

  • (*)λΌμΈμ—μ„œ λ©”μ†Œλ“œ tree.sayHiλ©”μ„œλ“œλŠ” rabbitμ—μ„œ λ³΅μ‚¬λ˜μ—ˆμŠ΅λ‹ˆλ‹€ μ½”λ“œ 쀑볡을 ν”Όν•˜κ³  μ‹Άμ—ˆμ„κΉŒ?
  • rabbitμ—μ„œ λ§Œλ“€μ–΄ 쑌기 λ•Œλ¬Έμ— [HomeObject](/Lee-hyuna/33-js-concepts-kr/wiki/HomeObject)λŠ” rabbit이닀. [HomeObject](/Lee-hyuna/33-js-concepts-kr/wiki/HomeObject)λ₯Ό λ³€κ²½ν•  수 μ—†λ‹€.
  • tree.sayHi()의 μ½”λ“œ 내뢀에 super.sayHi()κ°€ μžˆλ‹€. super.sayHi()λŠ” animal에 μžˆλŠ” λ©”μ†Œλ“œλ₯Ό κ°€μ Έμ˜¨λ‹€.

λ‹€μŒμ€ μ–΄λ–»κ²Œ μ§„ν–‰λ˜λŠ”μ§€μ— λŒ€ν•œ 그림이닀.

/resource/yongkwan/31/04.svg

λ©”μ„œλ“œλŠ” ν•¨μˆ˜(function) ν˜•νƒœκ°€ 되면 μ•ˆλœλ‹€.

ν΄λž˜μŠ€μ™€ 일반 κ°μ²΄μ—μ„œ [HomeObject](/Lee-hyuna/33-js-concepts-kr/wiki/HomeObject)λŠ” λ©”μ„œλ“œλ₯Ό μœ„ν•΄ μ •μ˜λœλ‹€. κ·ΈλŸ¬λ‚˜ 객체의 경우, λ©”μ„œλ“œκ°€ "method: function()"μ•„λ‹Œ method()둜 μ •ν™•ν•˜κ²Œ 지정 λ˜μ–΄μ•Όν•œλ‹€.

μš°λ¦¬κ°€ λŠλΌκΈ°μ—” μ€‘μš”ν•˜μ§€ μ•ŠλŠ” 것 처럼 λ³΄μ΄μ§€λ§Œ, JavaScriptμ—λŠ” μ€‘μš”ν•˜λ‹€.

μ•„λž˜ μ˜ˆμ œμ—μ„œ λΉ„ λ©”μ†Œλ“œ ꡬ문이 비ꡐ에 μ‚¬μš©λœλ‹€. [HomeObject](/Lee-hyuna/33-js-concepts-kr/wiki/HomeObject)속성이 μ„€μ •λ˜μ§€ μ•Šκ³  상속이 μž‘λ™ν•˜μ§€ μ•ŠλŠ”λ‹€.

let animal = {
  eat: function() { // intentially writing like this instead of eat() {...
    // ...
  }
};

let rabbit = {
  __proto__: animal,
  eat: function() {
    super.eat();
  }
};

rabbit.eat();  // Error calling super (because there's no [HomeObject](/Lee-hyuna/33-js-concepts-kr/wiki/HomeObject))

μš”μ•½

  1. Parent 클래슀λ₯Ό Child ν΄λž˜μŠ€κ°€ μƒμ†ν•˜λ €λ©΄ class Child extends Parent:
    • 이 μ˜λ―ΈλŠ” λ©”μ†Œλ“œκ°€ μƒμ†λ˜λ„λ‘ Child.prototype.__proto__이 Parent.prototype 될 κ²ƒμ΄λž€ μ˜λ―Έμ΄λ‹€.
  2. μƒμ„±μžλ₯Ό μ˜€λ²„λΌμ΄λ”© ν•˜λŠ” 경우 :
    • thisλ₯Ό μ‚¬μš©ν•˜κΈ°μ „μ— λ¨Όμ €, Child클래슀의 μƒμ„±μžμ—μ„œ super()λ₯Ό μ΄μš©ν•΄ λΆ€λͺ¨μ˜ μƒμ„±μžλ₯Ό ν˜ΈμΆœν•΄μ•Όν•œλ‹€.
  3. λ©”μ„œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ”©ν•˜λŠ” 경우 :
    • Childλ©”μ„œλ“œμ—μ„œ super.method()λ₯Ό μ‚¬μš©ν•΄ Parent λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•  수 μžˆλ‹€.
  4. λ‚΄λΆ€ :
    • λ©”μ„œλ“œλŠ” λ‚΄λΆ€ [HomeObject](/Lee-hyuna/33-js-concepts-kr/wiki/HomeObject)속성을 톡해 μžμ‹ μ˜ 클래슀/객체λ₯Ό κΈ°μ–΅ν•œλ‹€. 이것이 superλ₯Ό 톡해 λΆ€λͺ¨μ— 접근이 κ°€λŠ₯ν•œ μ΄μœ λ‹€.
    • λ”°λΌμ„œ ν•œ κ°μ²΄μ—μ„œ λ‹€λ₯Έ 객체둜 superλ₯Ό μ΄μš©ν•΄ λ©”μ„œλ“œλ₯Ό λ³΅μ‚¬ν•˜λŠ” 것은 μ•ˆμ „ν•˜μ§€ μ•Šλ‹€..

λ˜ν•œ:

  • ν™”μ‚΄ν‘œ ν•¨μˆ˜λŠ” thisλ˜λŠ” superλ₯Ό κ°–κ³  μžˆμ§€ μ•Šλ‹€. λ”°λΌμ„œ μ‚¬μš©ν• λ•ŒλŠ” μ£Όμ˜ν•˜μž.