TIL [프로토 타입]

프로토 타입 (Prototype) 이란?

1. 프로토 타입 vs Class

과거 클래스가 자바스크립트에 생기기 전 클래스의 기능을 하던 것이 바로 프로토 타입이다. 클래스가 없기 때문에 온전한 상속 기능이 없었고 이를 구현해내기 위해 프로토 타입을 이용했다.

2. 우리가 프로토 타입을 언제 쓸까?

클래스가 없을땐 함수와 new라는 키워드를 통해 클래스를 대신해서 썼다.

function Car(){
  this.wheel = 4;
  this.mirror = 2;
}

let car1 = new Car();
let car2 = new Car();

car1.wheel // 4
car1.mirror // 2
car2.wheel // 4
car2.mirror // 2

여기서 문제는 새로운 인스턴스가 생성될 때 마다 wheel과 mirror가 생성된다는 것이다. 지금은 상관없지만 인스턴스가 많으면 많을수록 wheel과 mirror도 많아질 것이다. 이러한 문제를 해결하기 위해 우리는 prototype을 이용할 수 있다.

function Car(){
 
}

Car.prototype.wheel = 4;
Car.prototype.mirror = 2;

let car1 = new Car();
let car2 = new Car();

car1.wheel // 4
car1.mirror // 2
car2.wheel // 4
car2.mirror // 2

모든 함수에는 프로토 타입이라는 특별한 객체가 있다. 따라서 Car.prototype이라는 객체도 메모리 상 어딘가에 존재하게 되는데, 이 객체에 wheel과 mirror라는 key를 설정하고 그 value로 각각 4와 2를 넣어준 것이다. 여기서 모든 함수에는 프로토 타입 객체가 존재한다고 그게 바로 다음에 나올 내용이다.

프로토 타입 객체 (Prototype Object)

1. 프로토 타입 객체 (Prototype Object)

function Car() {} // => 함수
let car1 = new Car(); // => 함수로 객체를 생성

car1이라는 객체는 Car라는 함수에서 생성되었다. 이렇듯 모든 객체는 함수에서 만들어진다. 우리가 자주 쓰는 객체들도 마찬가지 이다.

let obj = {};  
let obj = new Object();

위 둘은 똑같은 표현이다. 즉, obj라는 객체는 Object라는 내장 함수에서 만들어진 객체라는 뜻이다. Object라는 내장 함수에는 모든 함수가 그렇듯 Object.prototype이라는 객체가 메모리 상 어딘가에 존재하는데, 이 객체안에 toString이나 valueOf 같은 내장 메서드들이 들어있기 때문에 obj안에는 따로 이 메서드들이 입력되지 않아도 쓸 수 있는 것이다. 객체와 마찬가지로 함수와 배열 모두 함수를 통해 생성이 된다.

이 내용이 프로토 타입 객체와 무슨 관련이 있을까? 함수가 정의될 때 발생하는 2가지를 이해하면 된다.

1. 해당 함수에 Constructor(생성자) 자격을 부여한다.

Constructor 자격이 부여되면 new를 통해 객체를 만들어 낼 수 있다.

function Car() {} // => 함수
let car1 = new Car(); // => 함수로 객체를 생성
2. 해당 함수의 프로토 타입 객체의 생성 및 연결

함수를 정의하면 해당 함수의 프로토 타입 객체도 같이 생성되고 둘은 연결이 된다. 생성된 함수는 이제 prototype이라는 속성을 통해 프로토 타입 객체에 접근할 수 있다. 프로토 타입 객체는 일반적인 객체와 같으며 기본적으로 constructor와 __proto__ 라는 프로퍼티가 있다.

여기서 중요한 것은Car.prototype.constructor는 Car함수 자체를 가르키고 있다는 점이다. 프로토 타입객체 안의 constructor는 프로토 타입 객체의 짝인 함수를 가리키고 있다.

function Car(){
 
}

Car.prototype.wheel = 4;
Car.prototype.mirror = 2;

let car1 = new Car();
let car2 = new Car();

car1.wheel // 4
car1.mirror // 2
car2.wheel // 4
car2.mirror // 2

프로토 타입 객체는 일반적인 객체와 마찬가지로 프로퍼티를 마음대로 추가 및 삭제할 수 있다. car1과 car2는 Car 함수를 통해 생성되었으므로 Car.prototype을 참조할 수 있는 것이다.

프로토 타입 링크 (Prototype Link)

car1과 car2는 wheel과 mirror라는 속성이 없는데도 car1.wheel이나 car2.mirror가 가능하다. 그것은 바로 car1, car2가 가지고 있는 __proto__ 속성 때문이다. 모든 함수에는 프로토 타입이라는 객체가 생기는 것처럼 모든 객체는 __proto__라는 속성을 가지고 있다. __proto__는 객체가 생성될 때 쓰였던 함수의 프로토 타입 객체를 가르킨다.

실제로 콘솔창에 쳐보니 car1.__proto__는 Car.prototype을 가르키고 있다.

car1 객체가 wheel과 mirror라는 프로퍼티를 직접가지고 있지 않기 때문에 상위 프로토 타입 객체를 탐색한다. 만약 최상위인 Object함수의 프로토 타입 객체까지 확인해 보고 없으면 undefined를 반환하게 된다. 이렇게 객체의 __proto__속성을 통해 상위 프로토 타입 객체와 쭉 연결되어 결국 Object 함수의 프로토 타입 객체까지 닿는걸 프로토 타입 체인(prototype chain)이라고 한다.

프로토 타입 체인 (Prototype Chain)

function Human(name){
  this.name = name;
}
let steve = new Human('steve');
steve.__proto__ === Human.prototype;  // true
steve.__proto__.__proto__ === Object.prototype; // true
steve.toString() 
// 우리가 toString 이라는 메서드를 직접 선언해 주지 않아도 쓸 수 있는 이유는 
// Object라는 최상위 객체의 프로토 타입 객체에 __proto__로 접근해서 그 안의 toString 메서드를 가져왔기 때문이다.

모든 객체는 Object를 상속받기 때문에 Object.prototype안에 정의해 놓은 프로토 타입 메서드를 쓸 수 있는 것이다.

프로토 타입의 활용

1. 가짜 배열 만들기

let MyArray = function(){

}
MyArray.prototype = Array.prototype;
// Array도 함수이기 때문에 프로토 타입 객체가 존재
// MyArray함수의 프로토 타입 객체에  Array함수의 프로토 타입 객체를 할당
let myarr = new MyArray(); // 객체 생성자를 통해 객체 생성
myarr.push(1); // MyArray[1]

myarr은 배열이 아닌데도 배열처럼 작동한다.

좋은 웹페이지 즐겨찾기