JavaScript의 프로토타입 위임에 대한 쉬운 설명

JavaScript 언어는 functional programming 및 프로토타입 위임이라는 두 가지 기둥을 기반으로 합니다. JavaScript에서 classes를 추가하는 것은 개체 지향 프로그래밍 느낌을 주기 위한 구문 설탕에 불과합니다. 포장을 풀면 내부에서 함수를 찾을 수 있습니다.

프로토타입 위임



프로토타입 위임은 [[Prototype]] 체인의 상위 프로토타입에 책임을 위임하는 것입니다.

function foo(){}

Object.getPrototypeOf(foo) === Function.prototype; // true
Object.getPrototypeOf(Function.prototype) === Object.prototype; // true
Object.getPrototypeOf(Object.prototype); // null


프로토타입 체인은 다음과 같습니다.

foo --> Function.prototype --> Object.prototype --> null



간단히 말해서, 개체가 소유하지 않은 속성을 찾으려고 하면 JavaScript 엔진은 찾을 때까지 프로토타입 체인을 순회합니다. 이해를 돕기 위해 예를 들어보겠습니다.

const recipe = { name: "Garlic Naan" };

const recipeBook = {
  getRecipeName() {
    console.log(this.name);
  },
};

// Set 'recipeBook' as the prototype of 'recipe'
Object.setPrototypeOf(recipe, recipeBook);

// Prototypal delegation in action
recipe.getRecipeName(); // prints 'Garlic Naan'


프로토타입 체인은 다음과 같습니다.

recipe --> recipeBook -> Object.prototype --> null



개체recipegetRecipeName 속성을 소유하지 않습니다. 그러나 recipeBook를 프로토타입으로 설정하여 name를 인쇄하는 작업을 recipeBook 인스턴스에 위임했습니다. 이를 프로토타입 위임이라고 합니다.

이제 recipeorderRecipe()의 작업을 다른 인스턴스order에 위임하기를 원한다고 가정해 보겠습니다. 다음과 같이 프로토타입 체인을 확장하여 이를 수행할 수 있습니다.

const order = {
  orderRecipe() {
    console.log(`${this.name} ordered!`);
  },
};

// Extending the prototype chain
Object.setPrototypeOf(recipeBook, order);

recipe.orderRecipe(); // prints 'Garlic Naan ordered!'


프로토타입 체인은 다음으로 확장됩니다.

recipe --> recipeBook --> order --> Object.prototype --> null



이제 우리가 소유recipe.hasOwnProperty()라고 선언한 객체 리터럴이 하나도 없는데 왜 호출hasOwnProperty을 할 수 있는지 쉽게 설명할 수 있을 것 같습니다. 이는 모든 객체 리터럴이 Object.prototype 에서 암시적으로 상속되기 때문입니다. 이는 hasOwnProptery() 작업이 Object.protoype 에 위임되었음을 의미합니다.

전체 코드 예제는 다음과 같습니다.

const recipe = { name: "Garlic Naan" };
const recipeBook = {
  getRecipeName() {
    console.log(this.name);
  },
};

// Set 'recipeBook' as the prototype of 'recipe'
Object.setPrototypeOf(recipe, recipeBook);

const order = {
  orderRecipe() {
    console.log(`${this.name} ordered!`);
  },
};

// Extending the prototype chain
Object.setPrototypeOf(recipeBook, order);

// Prototypal delegation in action
recipe.getRecipeName(); // prints 'Garlic Naan'
recipe.orderRecipe(); // prints 'Garlic Naan ordered!'
recipe.hasOwnProperty("name"); //true


생성자 함수 및 new 키워드



이 위임 개념을 설명하기 전에 constructor functions과 인스턴스를 생성할 때 new 연산자를 사용해야 하는 이유에 대해서도 이야기하고 싶습니다. 프로토타입 개념을 제쳐두고 그들의 존재를 쉽게 이해할 수 있기를 바랍니다.

JavaScript의 모든 함수( except fat arrow )에는 prototype 속성이 있는 일반 객체인 constructor라는 속성이 있습니다. 이는 내부 [[Prototype]] 관계와 다릅니다.

이전recipe 예제를 다시 살펴보고 생성자 함수를 사용하여 동일한 프로토타입 관계를 설정하는 방법을 살펴보겠습니다.

// Constructor function 'Recipe'
function Recipe(name) {
  this.name;
}

Recipe.hasOwnProperty("prototype"); // true
Recipe.prototype.constructor === Recipe; // true


시각적으로 아래 다이어그램과 유사하게 보입니다.



속성(프로토타입)은 new 연산자를 사용하여 Recipe()를 호출할 때 new 연산자가 생성하는 인스턴스의 프로토타입으로 Recipe.prototype를 사용하기 때문에 특별합니다. 인스턴스가 생성되면 new 연산자는 해당 인스턴스를 this에 대한 매개변수 중 하나로 내부적으로 Recipe()로 전달합니다.

const recipe = new Recipe('Garlic Naan');




이제 Recipe.prototype에 속성을 추가해야 하는 이유가 명확해졌습니다. 속성은 프로토타입 위임을 통해 모든 Recipe 인스턴스에서 사용할 수 있게 됩니다.

__proto__ is a pseudo property for accessing the prototype of an object



// Adding properties to 'Recipe.prototype' will make them 
// available on all `Recipe` instances. 
Recipe.prototype.getRecipeName = function () {
  console.log(this.name);
}; 



마찬가지로 체인을 확장하고 레시피를 Recipe.prototype 프로토타입으로 설정하여 레시피를 다른 개체에 주문하는 작업을 위임할 수 있습니다.

// Order constructor
function Order() {}

Order.prototype.recipeOrder = {
  recipeOrder() {
    console.log(`${this.name} ordered!`);
  },
};

// Setting up the delegation aka Prototypal inheritance
Object.setPrototypeOf(Recipe.prototype, Order.prototype);

recipe.orderRecipe(); // prints 'Garlic Naan ordered!'


함수 생성자를 사용하는 전체 코드 예제는 다음과 같습니다.

// Constructor function 'Recipe'
function Recipe(name){this.name}

Recipe.hasOwnProperty('prototype'); // true
Recipe.prototype.constructor === Recipe; // true

const recipe = new Recipe('Garlic Naan');

Recipe.prototype.getName = function () {
  console.log(this.name);
};

// Order constructor
function Order() {}

Order.prototype.recipeOrder = {
  recipeOrder() {
    console.log(`${this.name} ordered!`);
  },
};

// Setting up the delegation aka Prototypal inheritance
Object.setPrototypeOf(Recipe.prototype, Order.prototype);

// Prototypal delegation in action
recipe.getRecipeName(); // prints 'Garlic Naan'
recipe.orderRecipe(); // prints 'Garlic Naan ordered!'
recipe.hasOwnProperty("name"); //true



최종 프로토타입 체인은 다음과 같습니다.



마무리 생각



JavaScript의 프로토타입은 처음에는 어려워 보일 수 있지만 이 기사가 학습 경로를 쉽게 만들었기를 바랍니다. JavaScript의 기초를 이해하는 것이 좋은 개발자가 되기 위한 열쇠입니다. 프로토타입 체인에 대해 자세히 알아보려면 Dr. Axel의 이 문서chapter를 읽어 보는 것이 좋습니다. 읽어주셔서 감사합니다 😍.

좋은 웹페이지 즐겨찾기