[JS] Prototype

Minyoung's Conference·2022년 5월 18일
0

JavaScript

목록 보기
9/28
post-thumbnail

prototype에 대해 알아보자.

function User(first, last) {
  this.firstName = first;
  this.lastName = last;  
}
User.prototype.getFullName = function () {
  return `${this.firstName} ${this.lastName}`;
};
const bradley = new User("Minyoung", "Kim");
const rachael = new User("Kayeon", "Kim");
console.log(bradley.getFullName());
console.log(rachael);

위 코드를 보면 prototype을 이용해 User라는 함수에 getFullName이라는 함수를 지정해준 걸 볼 수 있다.
그렇게 되면 console에 bradley와 rachael을 출력해보면,
getFullName이라는 함수가 담겨 있다는 것을 알 수 있다.

User라는 함수의 firstName과 lastName은 생성자함수가 생길 때마다 변화하는 걸 알 수 있다. 그래서 통일해서 관리하기 힘들지만,
getFullName이라는 함수는 로직이 같기 때문에 통일해서 메모리를 효율적으로 관리해주기 용이하다. 그렇기 떄문에 User라는 함수에 숨어있는 prototype이라는 속성을 활용해 getFullName을 할당해주면 수 많은 객체가 생겨나도 getFullName이라는 메소드는 메모리에 한번만 만들어져 있다.
또한 instance(bradley, rachael)들은 getFullName이라는 함수를 '참조'한다고 이야기할 수 있다. 또한 조금 더 정보를 보태자면, 자바스크립트라는 언어 자체는 prototype을 많이 사용하고 있다. 그래서 자바스크립트를 prototype 기반의 프로그래밍언어라고도 한다.


(간단하게 a라는 배열을 만들어보면 a라는 배열 내의 prototype 내에 수많은 메서드가 존재하는 것을 확인할 수 있다.)

처음 생성한 User 라는 함수에 메소드를 추가해보자.

function User(first, last) {
  this.firstName = first;
  this.lastName = last;
  this.getFullName = function(){
    return `${this.firstName} ${this.lastName}`;
  }
}

생성자 함수의 첫번째 글자는 파스칼케이스를 따라 대문자로 작성한다.
또한, 첫 글자를 대문자로 작성하기 때문에 생성자 함수를 만든다는 것을 직관적으로 알 수 있다. (관행적으로 오래도록 사용되고 있다.)

Prototype 내용 추가

(생성자함수 part를 읽고 오시면 이해가 더 쉽습니다.)

생성자 함수를 통해 잘나가는 공장장이 되었다. 하지만 넘쳐나는 쓰레기로 인해 양보다 질이 우선시 되는 다품종 소량생산의 시대가 되었다. 이제 좀 더 효율적인 객체 생산방식이 필요하다!

생성자를 통해 배웠던 것 중 하나의 질문이 있다.
console.log(myFood.smell === myFood2.smell); 

**이 두 인스턴스는 같은것일까? (같은 곳을 참조할까?)

아니다. 다른 곳을 참조한다 // false, 객체를 생성 할 때마다 별개의 함수가 계속 만들어 지는 것!
앞서 this에서 배운 내용을 바탕으로 좀 더 효율적인 공정으로 바꿔보자!

smell의 함수는 같기 때문에 바뀌지 않는다. 그래서 밖으로 빼주면 
인스턴스들이 같은 smell 함수를 참조하게 된다.**

function smell() {
  console.log(this.name + "맛이 난다!");
}

****function Food(name){
  this.name = name;
  this.smell = smell;
}

let myFood = new Food("로제파스타");
let myFood2 = new Food("토마토");
myFood.smell(); // 로제파스타맛이 난다!
myFood2.smell();// 토마토맛이 난다!
console.log(myFood.smell === myFood2.smell); //true

두 객체의 메서드는 같은 함수를 참조하게 되었다.

그런데, 이상한 점이 있다! 
myFood.constructor === Food // true 
myFood는 Food를 가리키고 있지만 constructor라는 프로퍼티는 어디서 온걸까?

**자바스크립트에서는 생성자의 prototype 프로퍼티를 통해 타입의 특징을 정의한다.**
앞서 본 constructor 메소드는 Object타입의 프로퍼티이며 prototype에 의해 정의 되었다.
(Object.prototype.constructor)

**그리고, 모든 인스턴스는 내부에 [[Prototype]] (=== __proto__)이라는 
프로퍼티를 가지며 이를 통해 생성자의 prototype 프로퍼티를 추적한다.**

그렇다면 일단 prototype을 사용해보자!

****Food.prototype.smell = function() {
	console.log(this.name + "냄새가 난다");
}

smell이라는 메서드가 prototype에 등록이 되었다.

그렇다면 Food 함수내에 smell 함수를 없애도 같게 나올까?

function Food(name) {
	this.name = name;
}
let myFood = new Food("로제파스타");
let myFood2 = new Food("토마토");
myFood.smell(); // 로제파스타맛이 난다!
myFood2.smell();// 토마토맛이 난다!
console.log(myFood.smell === myFood2.smell); //true

똑같이 나온다.

아래 그림을 보면 myFood, myFood2 모두 Prototype을 가지고 그 Prototype들은 
부모인 Food의 Prototype을 참조하고 있다. 
그렇기 떄문에 myFood에서도 smell이라는 프로퍼티가 있는 것처럼 사용할 수 있었다.

또한 부모인 Food 역시, 더 큰 부모인 Object의 prototype을 참조하고 있다.

우리가 따로 constructor라는 메소드를 생성자 함수에 넣지 않았지만 그 생성자 함수가 
Object 타입을 참조하고 있고 prototype도 참조하고 있기 때문에 그 아래의 인스턴스들도 
constructor 메소드를 자신의 프로퍼티처럼 사용할 수 있게 된 것이다.

이렇게, 인스턴스에서 생성자의 [[Prototype]]을 타고 올라가며 프로퍼티를 탐색하는 현상을
**프로토타입 체인**이라고 한다.

[정리]
- 자바스크립트는 생성자의 prototype 프로퍼티를 통해 타입의 특징을 정의한다.
- 모든 인스턴스는 내부에 [[Prototype]] 프로퍼티를 가지며 이를 통해 생성자의 prototype 프로퍼티를 추적한다.
- 인스턴스에서 생성자의 [[Prototype]]을 타고 올라가며 프로퍼티를 탐색하는 현상을 프로토타입 체인이라고 한다. 

(인프런 "코딩인터뷰를 저격하는 JS 스나이퍼 양성학교" 참고)

profile
안녕하세요, FE 개발자 김민영입니다.

0개의 댓글