본문 바로가기

자바스크립트

프로토타입 체이닝 (상속 흉내)

프로토타입(Prototype)이란?

자바스크립트에서 모든 객체는 자신의 부모 역할을 하는 객체와 연결되어 있다. (가장 최상위 부모 객체는 브라우저에서는 window, node.js에서는 global이다.)

자바스크립트에서는 프로토타입으로 객체지향 상속 개념을 구현하는데 이를 통해 부모 객체의 프로퍼티를 자식 객체가 사용할 수 있다. 프로토타입은 이런 식으로 사용되며 모든 객체에 존재한다.

 

자바스크립트의 객체 생성 규칙

자바스크립트에서 모든 객체는 자신을 생성한 생성자 함수의 prototype 프로퍼티가 가르키는 프로토타입 객체를 자신의 부모객체로 설정하는 [[prototype]] 링크로 연결한다.

함수객체와 프로토타입 객체는 서로를 바라보고 있다.

 

var Student = function (name, age) {
  this.name = name;
  this.age = age;
};

var freshman = new Student('Kim', 27);

함수를 생성할 때 prototype 프로퍼티로 자기 자신과 링크되는 프로토타입 객체를 생성한다.

따라서 Student 생성자 함수를 정의함으로써 Student.prototype 객체도 같이 생성되고, 해당 객체는 Student 생성자 함수prototype 프로퍼티 를 통해 접근이 가능하다.

 Student 생성자 함수를 통해 freshman 객체를 생성한다면,  freshman 객체Student.prototype 객체와 [[prototype]]링크를 통해 연결되기 때문에, 결국 prototype 프로퍼티 [[prototype]]링크는 같은 객체를 가리키게 된다.

객체를 생성하는 것은 생성자 함수의 몫이지만, 생성된 객체의 실제 부모 역할을 하는 것은 생성자 함수의 prototype 프로퍼티가 가르키는 프로토타입 객체라는 것을 알 수 있다.

 

프로토타입 체이닝(Prototype Chaining)이란?

프로토타입 체이닝이란 객체에서 자기 자신의 프로퍼티뿐만이 아니라, 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티도 자신의 프로퍼티처럼 사용할 수 있게 하는 것이다.

var student = {
  name: 'Kim',
  age: 27,
};

console.log(student.name); //Kim
console.log(student.valueOf()); //{ name:'Kim', age:27 }

이 예제에서는 2가지의 프로퍼티에 접근을 해봤다.

첫번째는 해당 객체에 존재하는 프로퍼티이기 때문에 당연히 출력이 되지만, 두 번째 프로퍼티는 존재하지도 않는데 결과가 정상적으로 출력이 된다. 이를 가능하게 해 주는 것이 바로 프로토타입 체이닝이다. valueOf() 메서드는 원시값을 반환해주는 표준 API이다.

 

객체 리터럴 방식으로 생성한 프로토타입 체이닝

* 객체 리터럴이란 여러가지 변수를 하나의 묶음으로 나타내는 객체를 표현할 때, 중괄호 {}를 사용하여 나타내는 방식이다.

리터럴 방식으로 생성된 객체는 실제로 Object 생성자 함수를 통해 객체가 생성된다. 따라서 위에서 설명한 자바스크립트의 객체 생성 규칙을 그대로 따라가 아래와 같이 관계가 형성된다.

프로토타입 체이닝의 개념으로 돌아가면 특정 객체의 프로퍼티에 접근하는 경우, 만약 자기 자신에게 해당 프로퍼티가 존재하지 않는다면 [[prototype]]링크로 연결된 프로토타입 객체에서 해당 프로퍼티를 검색하게 된다.

따라서 첫 번째로 name 프로퍼티를 출력했을 때에는 student 객체에 해당 프로퍼티가 존재하기 때문에 그대로 출력했지만, 두 번째로 valueOf() 메서드를 호출했을 때에는 student 객체에 해당 프로퍼티가 존재하지 않아 [[prototype]]링크를 타고 Object.prototype 객체에서 해당 프로퍼티를 찾는다.

 

생성자 함수로 생성한 객체의 프로토타입 체이닝

이 방식은 위의 객체 리터럴 방식으로 생성한 객체의 프로토타입 체이닝과는 약간 다르지만, 자바스크립트의 객체 생성 규칙을 그대로 따라간다. 즉, 생성자 함수를 통해 생성된 객체는 생성자 함수의 prototype 프로퍼티가 가리키는 프로토타입 객체를 자신의 부모 객체로 지정한다.

 

예시 1

var Student = function (name, age) {
  this.name = name;
  this.age = age;
};

var freshman = new Student('Kim', 27);

이렇게 생성된 freshman 객체는 프로토타입 체이닝을 통해 Student.prototype 객체까지 접근이 가능하지만, 프로토타입 체이닝은 여기서 끝나지 않는다.

결국 Student.prototype 객체도 자바스크립트 객체이므로 Object.prototype 객체와 [[prototype]]링크로 연결된다. 따라서 freshman 객체Object.prototype객체까지 프로토 타입 체이닝을 통해 접근이 가능하다.

 

 

예시 2

function Car(name, wheels){
		this.name = name;
    this.wheels = wheels;
}

Car.prototype.gasoline = function(x){return x + '리터 만큼 충전완료'}

//car 객체 생성
var benz = new Car('banz', 4);
var audi = new Car('audi', 4);

함수를 정의하면 아래 그림과 같이 Car() 생성자 함수프로토타입 객체도 함께 생성된다.

new 키워드를 이용해 benz 객체audi 객체를 생성한다.

여기서 두 new 객체 또한 프로토 타입 객체를 참조한다.

여기서 __proto__[[prototype]]링크라고도 하며, 프로토타입 객체와 연결된다.

여기서 출력을 해보면,

console.log(benz.gasoline(1)); //1리터 만큼 충전완료
console.log(audi.gasoline(2)); //2리터 만큼 충전완료

두 객체에 gasoline()이 정의되어있지 않지만 참조되어 있는 프로토타입 객체의 gasoline()이 있기 때문에 가능한 일이다.

자바스크립트는 gasoline()을 호출할 경우 먼저 객체 내부에서 값을 스캔한다. benz객체audi객체gasoline() 함수가 없을 경우, __proto__ 혹은 [[prototype]]링크를 타고 부모 역할을 하는 프로토타입 객체를 스캔한다. Car프로토타입 객체gasoline() 함수가 있으니 자바스크립트는 상속기능을 흉내 내 사용할 수 있다.

 

프로토타입 체이닝의 핵심

  • __proto__ 속성은 모든 객체들이 가지고 있다.
  • __proto__속성은 자신의 부모 역할을 하는 상위 프로토타입을 가리킨다.
  • 특정 속성을 찾을 때 상위 프로토타입을 타고 쭉 올라가 스캔하는데 이를 프로토타입 체이닝이라고 한다.
  • 최상위 프로토타입은 Object의 Prototype Object이다. 여기까지 스캔하고 없을 경우 undefined를 출력한다.

 

 

JavaScript 핵심 개념 - 프로토타입 체이닝#1 (Prototype Chaining)

[JS Core]JavaScript 프로토타입 체이닝(Prototype Link, Prototype Object)

 

'자바스크립트' 카테고리의 다른 글

Node.js의 동작방식  (0) 2023.04.05
동기적 실행 vs 비동기적 실행  (0) 2023.03.28
클로저 (private 흉내)  (0) 2023.03.21
this  (0) 2023.03.17
callback, promise, async/await  (0) 2023.03.14