[TECH-QA] ํ”„๋กœํ† ํƒ€์ž…(Prototype)๊ณผ ํ”„๋กœํ† ํƒ€์ž…์ฒด์ด๋‹

ํ”„๋กœํ† ํƒ€์ž…(prototype)

ํ”„๋กœํ† ํƒ€์ž…(prototype)์ด๋ž€ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ ๊ทธ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐ ์‚ฌ์šฉ๋œ ์›ํ˜•, ์ฆ‰ ํ…œํ”Œ๋ฆฟ ์—ญํ• ์„ ํ•˜๋Š” ํ”„๋กœํ† ํƒ€์ž… ๊ฐ์ฒด๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด๋Š” ์ด ํ”„๋กœํ† ํƒ€์ž… ๊ฐ์ฒด์™€ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์œผ๋ฉฐ, ์ด ์—ฐ๊ฒฐ์€ ๊ฐ์ฒด์˜ ์ˆจ๊ฒจ์ง„ ์†์„ฑ์ธ __proto__ (proto ์†์„ฑ)๋ฅผ ํ†ตํ•ด ์ฐธ์กฐ๋ฉ๋‹ˆ๋‹ค. ์ด __proto__ ์†์„ฑ์€ ํ•ด๋‹น ๊ฐ์ฒด๊ฐ€ ์–ด๋–ค ํ”„๋กœํ† ํƒ€์ž… ๊ฐ์ฒด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋งŒ๋“ค์–ด์กŒ๋Š”์ง€๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ๋งํฌ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ”„๋กœํ† ํƒ€์ž…์€ ๊ฐ์ฒด์™€ ๊ทธ ์›ํ˜• ๊ฐ์ฒด๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ํ•ต์‹ฌ์ ์ธ ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“ ๊ธฐ๋ณธ ๊ฐ์ฒด์˜ __proto__

// ๋นˆ ๊ฐ์ฒด ์ƒ์„ฑ
const obj = {};

console.log(obj.__proto__); // [Object: null prototype] {} (Object.prototype)

console.log(obj.__proto__.__proto__); // null

obj๋Š” ๊ธฐ๋ณธ ๊ฐ์ฒด๋กœ, Object ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ๋งŒ๋“ค์–ด์กŒ์Šต๋‹ˆ๋‹ค.
obj.__proto__๋Š” Object.prototype์„ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋ชจ๋“  ๊ธฐ๋ณธ ๊ฐ์ฒด์˜ ํ”„๋กœํ† ํƒ€์ž…์ž…๋‹ˆ๋‹ค.
Object.prototype์˜ __proto__๋Š” null๋กœ, ํ”„๋กœํ† ํƒ€์ž… ์ฒด์ธ์˜ ๋์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

๐Ÿ“ ๋ฐฐ์—ด ๊ฐ์ฒด์˜ __proto__

const arr = [1, 2, 3];

console.log(arr.__proto__); // [Array: null prototype] [] (Array.prototype)

console.log(arr.__proto__.__proto__); // [Object: null prototype] {} (Object.prototype)

console.log(arr.__proto__.__proto__.__proto__); // null
  • arr๋Š” ๋ฐฐ์—ด ๊ฐ์ฒด๋กœ, Array ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ๋งŒ๋“ค์–ด์กŒ์Šต๋‹ˆ๋‹ค.
  • arr.__proto__๋Š” Array.prototype์„ ์ฐธ์กฐํ•˜๋ฉฐ, ๋ฐฐ์—ด ๊ด€๋ จ ๋ฉ”์„œ๋“œ(push, pop ๋“ฑ)๊ฐ€ ์ด ํ”„๋กœํ† ํƒ€์ž…์— ์ •์˜๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • Array.prototype์˜ __proto__๋Š” Object.prototype์„ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ฐฐ์—ด๋„ ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ฐ์ฒด์˜ ์†์„ฑ์„ ์ƒ์†๋ฐ›์Šต๋‹ˆ๋‹ค.

๐Ÿ“ ์ปค์Šคํ…€ ๊ฐ์ฒด์™€ ์ƒ์„ฑ์ž ํ•จ์ˆ˜

// ์ƒ์„ฑ์ž ํ•จ์ˆ˜ ์ •์˜
function Person(name) {
  this.name = name;
}

// Person.prototype์— ๋ฉ”์„œ๋“œ ์ถ”๊ฐ€
Person.prototype.sayHello = function () {
  console.log("Hello, I'm ${this.name}");
};

// ๊ฐ์ฒด ์ƒ์„ฑ
const person = new Person("Alice");

// person์˜ __proto__ ํ™•์ธ
console.log(person.__proto__); // Person { sayHello: [Function (anonymous)] } (Person.prototype)

// Person.prototype์˜ __proto__ ํ™•์ธ
console.log(person.__proto__.__proto__); // [Object: null prototype] {} (Object.prototype)

// ํ”„๋กœํ† ํƒ€์ž… ์ฒด์ธ์„ ๋”ฐ๋ผ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
person.sayHello(); // Hello, I'm Alice
  • Person์€ ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋กœ, new Person()์œผ๋กœ ์ƒ์„ฑ๋œ person ๊ฐ์ฒด์˜ __proto__๋Š” Person.prototype์„ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค.
  • Person.prototype์—๋Š” sayHello ๋ฉ”์„œ๋“œ๊ฐ€ ์ •์˜๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ, person์€ ์ด ๋ฉ”์„œ๋“œ๋ฅผ ์ƒ์†๋ฐ›์•„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Person.prototype.__proto__๋Š” Object.prototype์„ ์ฐธ์กฐํ•˜๋ฉฐ, ์ฒด์ธ์€ ๊ฒฐ๊ตญ null๋กœ ๋๋‚ฉ๋‹ˆ๋‹ค.

__proto__๋Š” ๋น„ํ‘œ์ค€ ์†์„ฑ์ด์ง€๋งŒ, ๋Œ€๋ถ€๋ถ„์˜ ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋””๋ฒ„๊น… ์šฉ๋„๋กœ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
์‹ค์ œ ์ฝ”๋“œ์—์„œ๋Š” Object.getPrototypeOf(obj)๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ถŒ์žฅ๋ฉ๋‹ˆ๋‹ค.

console.log(Object.getPrototypeOf(person) === Person.prototype); // true
  • __proto__๋Š” ๊ฐ์ฒด์™€ ํ”„๋กœํ† ํƒ€์ž… ๊ฐ„์˜ ์—ฐ๊ฒฐ์„ ๋ณด์—ฌ์ฃผ๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด ํ”„๋กœํ† ํƒ€์ž… ์ฒด์ด๋‹์ด ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋กœํ† ํƒ€์ž… ์ฒด์ธ(prototype chain)

ํ”„๋กœํ† ํƒ€์ž… ์ฒด์ธ(prototype chain)์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด๋ฉด, ๊ฐ์ฒด์˜ __proto__ ์†์„ฑ์ด ๋˜ ๋‹ค๋ฅธ ๊ฐ์ฒด์˜ ํ”„๋กœํ† ํƒ€์ž…์„ ์ฐธ์กฐํ•˜๊ณ , ๊ทธ ์ฐธ์กฐ๋œ ํ”„๋กœํ† ํƒ€์ž… ๊ฐ์ฒด์˜ __proto__๊ฐ€ ๋‹ค์‹œ ๋˜ ๋‹ค๋ฅธ ํ”„๋กœํ† ํƒ€์ž…์„ ์ฐธ์กฐํ•˜๋Š” ์‹์œผ๋กœ ์—ฐ์‡„์ ์œผ๋กœ ์ด์–ด์ง„ ๊ตฌ์กฐ๋ฅผ ๋งํ•ฉ๋‹ˆ๋‹ค. ์ด ์ฒด์ธ์€ ์ตœ์ข…์ ์œผ๋กœ Object.prototype์ด๋ผ๋Š” ๊ธฐ๋ณธ ํ”„๋กœํ† ํƒ€์ž… ๊ฐ์ฒด์— ๋„๋‹ฌ ํ•  ๋•Œ๊นŒ์ง€ ์ด์–ด์ง€๋ฉฐ, ๊ทธ ๋์—๋Š” __proto__๊ฐ€ null์ธ ์ง€์ ์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋กœํ† ํƒ€์ž… ์ฒด์ด๋‹(prototype chaining)

์ด๋Ÿฌํ•œ ํ”„๋กœํ† ํƒ€์ž… ์ฒด์ธ์„ ํ™œ์šฉํ•˜์—ฌ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ์†์„ฑ์ด๋‚˜ ๋ฉ”์„œ๋“œ๋ฅผ ๊ฒ€์ƒ‰ํ•˜๋Š”๋ฐ, ์ด๋ฅผ ํ”„๋กœํ† ํƒ€์ž… ์ฒด์ด๋‹(prototype chaining)์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๊ฐ์ฒด์—์„œ ํŠน์ • ์†์„ฑ์ด๋‚˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ ค ํ•  ๋•Œ, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์—”์ง„์€ ๋จผ์ € ๊ทธ ๊ฐ์ฒด ์ž์ฒด์—์„œ ํ•ด๋‹น ์†์„ฑ์„ ์ฐพ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์ฐพ์ง€ ๋ชปํ•˜๋ฉด __proto__๋ฅผ ํ†ตํ•ด ์—ฐ๊ฒฐ๋œ ํ”„๋กœํ† ํƒ€์ž… ๊ฐ์ฒด๋กœ ์ด๋™ํ•˜์—ฌ ๊ฒ€์ƒ‰์„ ๊ณ„์†ํ•˜๊ณ , ์ด ๊ณผ์ •์„ ์ฒด์ธ์„ ๋”ฐ๋ผ ๋๊นŒ์ง€ ๋ฐ˜๋ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์ฒด์ธ์„ ๋”ฐ๋ผ๊ฐ€๋ฉฐ ์†์„ฑ์„ ํƒ์ƒ‰ํ•˜๋Š” ๋ฐฉ์‹์ด ํ”„๋กœํ† ํƒ€์ž… ์ฒด์ด๋‹์˜ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค.

ํ”„๋กœํ† ํƒ€์ž… ๊ธฐ๋ฐ˜ ์ƒ์†(prototype-based inheritance)

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ๊ฐ์ฒด๋Š” ์ž์‹ ์˜ ํ”„๋กœํ† ํƒ€์ž… ๊ฐ์ฒด๋กœ๋ถ€ํ„ฐ ์†์„ฑ๊ณผ ๋ฉ”์„œ๋“œ๋ฅผ "์ƒ์†"๋ฐ›์•„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋™์  ์—ฐ๊ฒฐ: ๊ฐ์ฒด๋Š” __proto__๋ฅผ ํ†ตํ•ด ํ”„๋กœํ† ํƒ€์ž… ๊ฐ์ฒด์™€ ์—ฐ๊ฒฐ๋˜๋ฉฐ, ์ด ์—ฐ๊ฒฐ์€ ๋Ÿฐํƒ€์ž„์— ๋™์ ์œผ๋กœ ์†์„ฑ์ด๋‚˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ณต์œ : ํ”„๋กœํ† ํƒ€์ž…์— ์ •์˜๋œ ์†์„ฑ์ด๋‚˜ ๋ฉ”์„œ๋“œ๋Š” ํ•ด๋‹น ํ”„๋กœํ† ํƒ€์ž…์„ ์ƒ์†๋ฐ›๋Š” ๋ชจ๋“  ๊ฐ์ฒด๊ฐ€ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ๊ฐ ๊ฐ์ฒด๋งˆ๋‹ค ์ƒˆ๋กœ ์ƒ์„ฑ๋˜์ง€ ์•Š๊ณ  ํ”„๋กœํ† ํƒ€์ž…์—์„œ ์ฐธ์กฐ๋ฉ๋‹ˆ๋‹ค.
  • ์ฒด์ธ ๊ตฌ์กฐ: ์ƒ์†์€ ๋‹จ์ผ ๊ฐ์ฒด ์ˆ˜์ค€์—์„œ ๋๋‚˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ํ”„๋กœํ† ํƒ€์ž… ์ฒด์ธ์„ ๋”ฐ๋ผ ์—ฌ๋Ÿฌ ๋‹จ๊ณ„๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“ ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ์ƒ์†์œผ๋กœ ์ดํ•ดํ•˜๋Š” ์˜ˆ์‹œ

// ๋ถ€๋ชจ ์—ญํ• ์„ ํ•  ํ”„๋กœํ† ํƒ€์ž… ๊ฐ์ฒด
const animal = {
  eat: function() {
    console.log("${this.name} is eating!");
  }
};

// ์ž์‹ ๊ฐ์ฒด ์ƒ์„ฑ ๋ฐ ์ƒ์† ์„ค์ •
const dog = {
  name: "Dog"
};
Object.setPrototypeOf(dog, animal);

// ์ƒ์†๋ฐ›์€ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
dog.eat(); 
// ์ถœ๋ ฅ: "Dog is eating!"

// ํ”„๋กœํ† ํƒ€์ž… ํ™•์ธ
console.log(dog.__proto__ === animal); 
// ์ถœ๋ ฅ: true
  • dog ๊ฐ์ฒด๋Š” animal ๊ฐ์ฒด๋ฅผ ํ”„๋กœํ† ํƒ€์ž…์œผ๋กœ ์ƒ์†๋ฐ›์•˜์Šต๋‹ˆ๋‹ค.
  • dog ์ž์ฒด์—๋Š” eat ๋ฉ”์„œ๋“œ๊ฐ€ ์—†์ง€๋งŒ, __proto__๋ฅผ ํ†ตํ•ด animal์˜ eat ๋ฉ”์„œ๋“œ๋ฅผ ์ƒ์†๋ฐ›์•„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“ ์ƒ์„ฑ์ž ํ•จ์ˆ˜๋ฅผ ํ†ตํ•œ ์ƒ์† ์˜ˆ์‹œ

// ๋ถ€๋ชจ ์ƒ์„ฑ์ž ํ•จ์ˆ˜
function Animal(name) {
  this.name = name;
}
Animal.prototype.eat = function() {
  console.log("${this.name} is eating!");
};

// ์ž์‹ ์ƒ์„ฑ์ž ํ•จ์ˆ˜
function Dog(name) {
  Animal.call(this, name); // Animal์˜ ์†์„ฑ ์ƒ์†
}
Object.setPrototypeOf(Dog.prototype, Animal.prototype); // ํ”„๋กœํ† ํƒ€์ž… ์ƒ์†

// ๊ฐ์ฒด ์ƒ์„ฑ
const myDog = new Dog("Max");

// ์ƒ์†๋ฐ›์€ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
myDog.eat(); 
// ์ถœ๋ ฅ: "Max is eating!"

// ํ”„๋กœํ† ํƒ€์ž… ์ฒด์ธ ํ™•์ธ
console.log(myDog.__proto__ === Dog.prototype); 
// ์ถœ๋ ฅ: true
console.log(myDog.__proto__.__proto__ === Animal.prototype); 
// ์ถœ๋ ฅ: true
  • Dog๋Š” Animal์˜ ํ”„๋กœํ† ํƒ€์ž…์„ ์ƒ์†๋ฐ›์•„ eat ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Animal.call(this, name)์œผ๋กœ name ์†์„ฑ์„ ์ƒ์†๋ฐ›๊ณ , Object.setPrototypeOf๋กœ ํ”„๋กœํ† ํƒ€์ž… ์ฒด์ธ์„ ์—ฐ๊ฒฐํ•ด ๋ฉ”์„œ๋“œ๊นŒ์ง€ ์ƒ์†๋ฐ›์•˜์Šต๋‹ˆ๋‹ค.
  • ์ด๋Š” ์ „ํ†ต์ ์ธ ์ƒ์†์ฒ˜๋Ÿผ "๋ถ€๋ชจ(Animal)๋กœ๋ถ€ํ„ฐ ์ž์‹(Dog)์ด ๊ธฐ๋Šฅ์„ ๋ฌผ๋ ค๋ฐ›๋Š”๋‹ค"๋Š” ๊ฐœ๋…์œผ๋กœ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ƒ์†์œผ๋กœ ๋ณผ ์ˆ˜ ์žˆ๋Š” ์ด์œ 

  • ์†์„ฑ๊ณผ ๋ฉ”์„œ๋“œ ์žฌ์‚ฌ์šฉ: ๊ฐ์ฒด๊ฐ€ ํ”„๋กœํ† ํƒ€์ž…์—์„œ ์†์„ฑ๊ณผ ๋ฉ”์„œ๋“œ๋ฅผ ๋ฌผ๋ ค๋ฐ›์•„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, ์ฝ”๋“œ ์ค‘๋ณต์„ ์ค„์ด๊ณ  ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์ด๋Š” ์ƒ์†์˜ ๋ชฉ์ ์„ ์ถฉ์กฑํ•ฉ๋‹ˆ๋‹ค.
  • ๊ณ„์ธต ๊ตฌ์กฐ: ํ”„๋กœํ† ํƒ€์ž… ์ฒด์ธ์„ ํ†ตํ•ด ๋ถ€๋ชจ-์ž์‹ ๊ด€๊ณ„์™€ ์œ ์‚ฌํ•œ ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ํ˜•์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • ํ™•์žฅ ๊ฐ€๋Šฅ์„ฑ: ํ”„๋กœํ† ํƒ€์ž…์— ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ์ด๋ฅผ ์ƒ์†๋ฐ›๋Š” ๋ชจ๋“  ๊ฐ์ฒด๊ฐ€ ๊ทธ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด, ์ƒ์†์˜ ์œ ์—ฐ์„ฑ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

์ฃผ์˜ํ•  ์ 
์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ์ƒ์†์€ ํด๋ž˜์Šค ๊ธฐ๋ฐ˜ ์–ธ์–ด์ฒ˜๋Ÿผ ๊ณ ์ •๋œ ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ๋”ฐ๋ฅด์ง€ ์•Š๊ณ , ๊ฐ์ฒด ๊ฐ„์˜ ๋™์  ์—ฐ๊ฒฐ(__proto__)์— ์˜์กดํ•ฉ๋‹ˆ๋‹ค. ์ƒ์†๋ฐ›์€ ์†์„ฑ์ด๋‚˜ ๋ฉ”์„œ๋“œ๋Š” ์‹ค์ œ๋กœ ๊ฐ์ฒด ์ž์ฒด์— ๋ณต์‚ฌ๋˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์ฐธ์กฐ๋˜๋Š” ๊ฒƒ์ด๋ฏ€๋กœ, ํ”„๋กœํ† ํƒ€์ž…์„ ์ˆ˜์ •ํ•˜๋ฉด ๋ชจ๋“  ์ƒ์†๋ฐ›์€ ๊ฐ์ฒด์— ์˜ํ–ฅ์„ ๋ฏธ์นฉ๋‹ˆ๋‹ค.