본문 바로가기

Languages/JavaScript

[JavaScript] 프로토타입 (Prototype)

728x90

 

 

 목차 

1. 프로토타입 (prototype)
2. [[Prototype]]
3. 프로토타입 상속 
4. __proto__
5. 프로토타입 메서드 
6. 프로토타입 체인 
7. 함수의 prototype 프로퍼티
8. "prototype"과 [[Prototype]]의 차이
9. constructor 프로퍼티
10. 상속 트리

 

 

 

 

"어렵고 추상적인...(사실 프로토타입 개념이 넘 어려워서 미뤄왔다가 피할 수 없어서 이제야 씀 ㅎ..)

프로토타입에 대해서 핵심 위주로 알아보자!"

 

 

 

 


 

 

 

 

 1. 프로토타입(prototype) 

 

- 자바스크립트의 모든 객체는 최소한 하나 이상의 다른 객체로부터 상속을 받는데, 이때 상속되는 정보를 제공하는 객체

- "생성자 함수에만 존재하는 속성"이며, 해당 생성자 함수로부터 생성된 객체들이 공유할 프로퍼티와 메서드를 정의하는 데 사용됨

 

 

 Q. prototype은 "속성" 이라고 봐야할까? 아니면 "객체" 라고 봐야할까?

 

"prototype"은 둘 다를 포함한 개념이다.

첫째로, "prototype"은 생성자 함수 객체의 속성(property)이다. 이 속성은 해당 생성자 함수로부터 생성된 객체들이 상속받을 프로퍼티와 메서드를 정의하는 데 사용된다. 따라서 "prototype"은 생성자 함수 객체의 속성(property)이며, 해당 속성에는 해당 생성자 함수로부터 생성된 객체들이 공유하는 프로퍼티와 메서드가 포함된다.

둘째로, "prototype"은 또한 생성자 함수로부터 생성된 객체들의 내부 [[Prototype]] 링크를 가리키는 객체이다. 모든 객체는 내부적으로 프로토타입을 가리키는 링크를 가지고 있는데, 이를 통해 프로토타입 체인을 형성하고 객체들 간의 상속을 구현한다. 이러한 의미에서 "prototype"은 객체(object)이기도 하다.

따라서 "prototype"은 생성자 함수 객체의 속성(property)이자, 해당 생성자 함수로부터 생성된 객체들의 내부 [[Prototype]] 링크를 가리키는 객체이다.




 

 

 2. [[Prototype]] 

 

- 모든 객체가 가지고 있는 내부 프로퍼티로, 객체의 프로토타입을 가리키는 참조

- 해당 객체의 프로토타입


 


 

 

 3. 프로토타입 상속 


- 현재 존재하고 있는 객체를 프로토타입으로 사용하여, 해당 객체를 복제하여 재사용하는 것

- 객체에서 프로퍼티를 읽으려고 하는데 해당 프로퍼티가 없으면 자바스크립트는 자동으로 프로토타입에서 프로퍼티를 찾음 (객체가 다른 객체의 프로퍼티와 메서드를 상속받는 메커니즘)

 

 


 

 

 4. __proto__ 

 

[[Prototype]]의 getter(획득자)이자 setter(설정자) 

__proto__을 사용하면 값을 설정할 수 있음                                                                        

let animal = {
  eats: true
};
let rabbit = {
  jumps: true
};

rabbit.__proto__ = animal; // animal이 rabbit의 프로토타입이 되도록 설정

// 프로퍼티 eats과 jumps를 rabbit에서도 사용할 수 있게 되었습니다.
alert( rabbit.eats ); // true  // [[Prototype]]이 참조하고 있는 객체인 animal에서 eats를 얻어냄
alert( rabbit.jumps ); // true

 

- rabbit의 프로토타입은 animal이다.  = rabbit은 animal을 상속받는다.
- rabbit에서도 animal에 구현된 유용한 프로퍼티와 메서드를 사용할 수 있다.
- 프로토타입에서 상속받은 프로퍼티를 '상속 프로퍼티(inherited property)'라고 한다. (여기선 animal 객체의 eats 프로퍼티)

 







 5. 프로토타입 메서드

 

__proto__는 브라우저를 대상으로 개발하고 있다면 다소 구식이기 때문에 더는 사용하지 않는 것이 좋다. 

대신 아래와 같은 모던한 메서드들을 사용하는 것이 좋다.

Object.create(proto, [descriptors]) :  [[Prototype]]이 proto를 참조하는 빈 객체를 만든다. 이때 프로퍼티 설명자를 추가로 넘길 수 있다.

Object.getPrototypeOf(obj)  :  obj의 [[Prototype]]을 반환한다.

Object.setPrototypeOf(obj, proto)  :  obj의 [[Prototype]]이 proto가 되도록 설정한다.

 

let animal = {
  eats: true
};

// 프로토타입이 animal인 새로운 객체를 생성한다. 
let rabbit = Object.create(animal);

// 참고) 위와 같은 말 ( __proto__ 사용)
let rabbit = {};
rabbit.__proto__ = animal;


alert(rabbit.eats); // true

alert(Object.getPrototypeOf(rabbit) === animal); // true

Object.setPrototypeOf(rabbit, {}); // rabbit의 프로토타입을 {}으로 바꾼다.

 

 


 

 

 6. 프로토타입 체인 


- 객체 간의 상속 관계를 이루는 메커니즘

- 객체가 자신의 프로토타입을 찾을 때까지 (프로토타입이 상속되는) 가상의 연결 고리를 따라 올라가며 검색하는 과정

 

let animal = {
  eats: true,
  walk() {
    alert("동물이 걷습니다.");
  }
};

let rabbit = {
  jumps: true,
  __proto__: animal
};

let longEar = {
  earLength: 10,
  __proto__: rabbit
};

// 메서드 walk는 프로토타입 체인을 통해 상속받음
longEar.walk(); // 동물이 걷습니다.
alert(longEar.jumps); // true (rabbit에서 상속받음)



 


 

 

 7. 함수의 prototype 프로퍼티 

 

- 생성자 함수를 사용해 객체를 만든 경우에 프로토타입이 어떻게 동작하는지 알아보자.

- 생성자 함수의 프로토타입이 객체인 경우에 new 연산자를 사용해 만든 객체는 생성자 함수의 프로토타입 정보를 사용해 [[Prototype]]을 설정한다

 

let animal = {
  eats: true
};

function Rabbit(name) {
  this.name = name;
}

Rabbit.prototype = animal; // new Rabbit을 호출해 만든 새로운 객체의 [[Prototype]]을 animal로 설정하라

let rabbit = new Rabbit("흰 토끼"); //  rabbit.__proto__ == animal

console.log( rabbit.name ) // 흰 토끼

console.log( rabbit.eats ); // true

 

 


 

 

 8. "prototype"과 "[[Prototype]]"의 차이 

 

// 생성자 함수
function Person(name) {
  this.name = name;
}

// Person 함수의 prototype에 greet 메서드 추가
Person.prototype.greet = function() {
  console.log('Hello, my name is ' + this.name);
};

// 객체 생성
var person1 = new Person('Alice');

// 객체의 프로토타입 확인
console.log(person1.__proto__ === Person.prototype); // true
console.log(person1.constructor === Person); // true

// 객체의 프로토타입에 접근
console.log(Person.prototype); // Person {} (greet 메서드가 포함됨)

// 객체의 내부 [[Prototype]] 확인
console.log(Object.getPrototypeOf(person1) === Person.prototype); // true

// 객체의 프로토타입 메서드 호출
person1.greet(); // "Hello, my name is Alice"

// 객체의 프로토타입 속성 확인
console.log(person1.hasOwnProperty('name')); // true (인스턴스 속성)
console.log(person1.hasOwnProperty('greet')); // false (프로토타입 속성)

 

이 코드에서는 "Person"이라는 생성자 함수를 정의하고, 그 함수의 "prototype" 속성에 "greet" 메서드를 추가했다.

그리고 "person1"이라는 객체를 "Person" 생성자 함수를 사용하여 생성하였다.

여기서 "prototype"은 생성자 함수에만 존재하는 속성으로, 해당 생성자 함수로부터 생성된 객체들이 공유할 프로퍼티와 메서드를 정의하는 데 사용된다.

따라서 "person1.greet()"과 같이 객체의 메서드를 호출할 때는 해당 객체의 프로토타입인 "Person.prototype"에서 메서드를 찾는다.

그리고 "[[Prototype]]"은 모든 객체가 가지고 있는 내부 프로퍼티로, 해당 객체의 프로토타입을 가리킨다.
"person1" 객체의 내부 "[[Prototype]]"은 "Person.prototype"을 가리키고 있으며, 이를 통해 상속된 메서드와 프로퍼티에 접근할 수 있다.

결과적으로, "prototype"은 생성자 함수가 가지고 있는 속성이고, "[[Prototype]]"은 모든 객체가 가지고 있는 내부 프로퍼티로, 이를 통해 객체 간의 상속 관계가 형성된다. 


 

 Q1. console.log(person1.hasOwnProperty('greet')); 가 false인 이유

hasOwnProperty 메소드가 해당 객체의 직접적인 속성만을 검사하기 때문이다.

greet 메소드는 person1 객체의 직접적인 속성이 아니라 Person.prototype 객체의 속성이다.
이 메소드는 Person.prototype에 정의되어 있고, person1 객체는 이를 프로토타입 체인을 통해 상속받아 사용하고 있다.

따라서 person1.hasOwnProperty('greet')는 person1 객체가 직접적으로 greet 속성을 가지고 있는지를 확인하는 것이기 때문에 false가 반환된다.

 

 Q2. console.log(person1.hasOwnProperty('name'));가 true인 이유 

name 속성이 person1 객체의 직접적인 속성으로 정의되었기 때문이다.

생성자 함수 Person의 실행 과정에서 this.name = name; 코드를 통해 person1 객체에 name 속성이 직접적으로 정의되엇다.
따라서 person1.hasOwnProperty('name')는 person1 객체가 직접적으로 name 속성을 가지고 있는지를 확인하는 것이기 때문에 true가 반환된다. 


 

 


 

 

 9. constructor 프로퍼티 


- 해당 객체를 생성한 생성자 함수를 참조하는 프로퍼티

- 객체가 생성될 때 해당 생성자 함수를 가리키게 된다.

function Person(name) {
  this.name = name;
}

let person1 = new Person('Alice');

console.log(person1.constructor); // Person 함수를 가리킴

 

위 코드에서 Person 함수를 사용하여 person1 객체를 생성했다.

이때 person1.constructor는 Person 함수를 가리키게 된다.

이는 person1 객체가 Person 함수에 의해 생성되었음을 나타낸다.

constructor 프로퍼티는 주로 객체가 자신을 생성한 생성자 함수를 알고 싶을 때 사용된다.

또한 프로토타입 상속에서 객체의 생성자를 확인하고 수정하는 데 사용될 수도 있다.


 


 

 

 10. 상속 트리 

let arr = [1, 2, 3];

// arr은 Array.prototype을 상속받았나요?
alert( arr.__proto__ === Array.prototype ); // true

// arr은 (Array.prototype 위에 있는) Object.prototype을 상속받았나요?
alert( arr.__proto__.__proto__ === Object.prototype ); // true

// 체인 맨 위엔 null이 있습니다.
alert( arr.__proto__.__proto__.__proto__ ); // null

 

- 모든 내장 프로토타입의 상속 트리 꼭대기엔 Object.prototype이 있어야 한다
-  Object.prototype 위쪽엔 [[Prototype]] 체인이 없다.  Object.prototype.__proto__; // null

- 예시) 배열 [1, 2, 3]을 만들면 new Array()의 디폴트 생성자가 내부에서 동작하여 Array.prototype이 배열 [1, 2, 3]의 프로토타입이 되고 개발자는 Array.prototype을 통해 배열 메서드를 사용할 수 있다.

- 체인 상의 프로토타입엔 중복 메서드가 있을 때는  체인 상에서 가까운 곳에 있는 메서드가 사용된다. 


 

 

728x90