지난 글에서 객체지향언어에서 핵심이 되는 개념인 클래스(Class)에 대해 소개했다. Javascript도 객체지향언어이다. 하지만 클래스라는 개념이 없을뿐.. 대신 Javascript에는 프로토타입(Prototype)이라는 개념이 존재한다!
객체 지향 프로그래밍의 네 가지 특징 중 '상속성' 이라는 게 있다는 것도 기억할 것이다. 상속성은 부모클래스의 속성과 기능을 그대로 이어받아 사용할 수 있게하고, 기능의 일부분을 변경해야 할 경우 상속받은 자식클래스에서 해당 기능만 재정의할 수 있게 하는 객체 지향 프로그래밍의 특성이다.
클래스라는 개념이 존재하는 Class 기반 언어들은 이러한 상속을 사용한다. 하지만 Javascript는 이와 달리 Prototype 기반 언어이다. 클래스가 없기 때문에 상속같은 기능도 따로 존재하지 않는다. 그래서 보통 이런 Prototype 기반언어는 프로토타입이라는 것을 가지고 상속을 흉내내곤 한다.
조금만 더 구체적으로 말하면, Prototype 기반 언어는 어떤 객체를 원형(prototype)으로 삼고, 그 객체를 복제해 상속과 비슷한 효과를 낸다. prototype은 무언가를 찍어낼 때 기준이 되는 틀 같은 것이라고 할 수 있다.
Prototype
Javascript에는 비록 클래스는 없지만 아래처럼 함수와 new 키워드를 이용해 클래스를 흉내낼 수 있다. 함수가 정의될 때는 해당 함수에 Constructor(생성자) 자격이 부여되고, 이렇게 자격을 부여받으면 new를 통해 객체를 만들어 낼 수 있게 된다. 객체는 항상 함수로만 생성되기 때문에 new 키워드 또한 함수만 사용할 수 있다.
var instance = new Constructor();
위에 있는 코드는 겨우 한 줄이지만 이 안에는 아래와 같은 과정들이 포함되어 있다.
1) Constructor(생성자함수)를 new로 호출
2) Constructor 내용을 기반으로 새 instance 생성
3) instance에는 __proto__라는 속성이 부여됨
4) 이 속성은 Constructor의 prototype 속성을 참조함
Constructor, prototype, instance, __proto__ 의 관계를 간단히 도식화시켜보면 아래와 같다. Constructor은 new 키워드로 instance를 생성하고, instance의 속성인 __proto__는 Constructor의 속성인 prototype을 참조한다!
prototype 과 __proto__
Constructor(생성자함수)와 instance(인스턴스)의 속성으로 각각 부여되는 prototype, __proto__ 객체들에 대해 좀 더 알아보자.
prototype 객체에는 생성될 인스턴스들이 사용할 수 있는 메서드들이 저장되어 있다. 앞서 언급했듯이 인스턴스의 속성인 __proto__가 이 prototype을 참조하기 때문에, instance는 Constructor의 메서드를 사용할 수 있게 된다.
예시를 한번 보자. Person이라는 Constructor가 jooing이라는 instance를 생성했다. 그리고 __proto__ 속성을 통해 getName이라는 Person의 메서드를 사용했다. 그런데 결과값으로 undefined가 나온 걸 확인할 수 있다.
var Person = function(name){
this._name = name;
};
Person.prototype.getName = function(){
return this._name;
}
var jooing = new Person('주혜');
jooing.__proto__.getName(); // undefined
분명 __proto__속성 덕분에 Constructor의 메서드를 사용할 수 있다고 했고, 아래처럼 prototype과 __proto__이 같은 값을 참조한다는 것도 확인할 수 있다. 그런데 왜 이런 결과가 나온것일까?
Person.prototype === jooing.__proto__ // true
this와 관련해 생각해보면 정확한 원인이 나온다. 여기서 getName메서드의 this값은 상위 객체인 jooing이다. 하지만 undefined가 나왔던 때를 보면 getName메서드의 this값은 jooing.__proto__였기 때문에 원하는 값이 나오지 않았던 것이다.
jooing.__proto__.getName(); // undefined
jooing.getName(); // 주혜
사실 __proto__는 생략 가능한 속성이라고 한다. (prototype객체 내부에 저장된 getName메서드를 쓰려면 __proto__을 통해 접근해야 할 것 같아서 이상할 수 있지만 Javascript가 그렇게 정했다고 한다..) 그래서 jooing.__proto__.getName 에서 속성 부분을 생략하고 그냥 jooing.getName 처럼 써주면 우리가 원하던 결과를 얻을 수 있는 것이다. 아래 도식화한 그림을 보면 __proto__속성이 생략되면, instance가 prototype을 참조하게 되는 것을 확인할 수 있다.
여기까지의 내용을 정리해보자.
1. Constructor를 new 연산자로 호출하면 instance가 만들어진다.
2. instance의 속성인 __proto__는 Constructor의 prototype을 참조한다.
3. __proto__는 생략 가능하다.
결국은 Constructor의 prototype에 어떤 속성이나 메서드가 있다면 instance도 마치 자기것인 것 마냥 그 속성, 메서드에 접근할 수 있게 되는 것이다. 상속과 굉장히 비슷하게 구현되었다는 것을 확인할 수 있다.
눈으로 확인해보기 👀
prototype과 __proto__를 눈으로 확인해보는 과정이라 내용을 이해했다면 이 부분은 넘어가도 괜찮다.
var Constructor = function(name){
this.name = name;
}
Constructor.prototype.func = function(){};
Constructor.prototype.prop = 'property';
var instance = new Constructor('주혜');
console.dir(Constructor);
console.dir(instance);
Constructor을 출력한 결과이다. prototype 속성이 있는 것을 확인할 수 있다. 그리고 prototype 속성을 한 번 더 열어보면, 그 안에 constructor라는 속성이 있는데 잠시 후에 이 속성에 대해서도 알아보려하니 이런게 있구나 기억해두고 넘어가자.
이번에는 instance를 출력한 결과인데, 출력 결과로 Constructor가 나오고 있다. 또 내용을 보면 위와는 다르게 __proto__ 속성만 존재한다. 이를 통해 instance는 자신을 생성한 Constructor의 인스턴스라는 것을 명시적으로 알린다는 것을 확인할 수 있다.
constructor 속성
Constructor(생성자함수)와 instance 객체에는 constructor이라는 속성이 존재한다. (바로 위의 콘솔창을 다시한번 확인해보자)
constructor는 자기 자신, 즉 생성자 함수를 참조하는 속성이다. 굳이 왜 자기 자신을 참조하는걸까? 바로 instance 때문이다. instance의 원형이 되는 생성자함수가 무엇인지 알게 해주는 역할을 바로 이 속성이 하고 있었다.
코드를 통해 살펴보자. 우선은 Person이라는 Constructor(생성자 함수)로 jooing이라는 instance(인스턴스)를 생성해두었다.
Person = function (name, age) {
this.name = name;
this.age = age;
}
jooing = new Person('김주혜', 18);
이 인스턴스를 생성한 함수를 확인하기 위해 constructor 속성을 사용해보았다. 아래의 출력 결과를 보면 생성자 함수인 Person함수가 나온 것을 볼 수 있다. 이처럼 constructor속성을 확인해보면, 누가(어떤 생성자함수가) instance를 만든 것인지 추적할 수 있게 된다. 이런 추적이 가능한 덕분에 instance의 속성이 생성자 함수의 속성을 참조할 수 있게 된 것이고, 이 덕분에 상속을 흉내내는 것 까지도 가능하게 된 것이다.
여기까지 프로토타입기반 언어인 Javascript에서 Prototype을 통해 어떤 식으로 객체지향 프로그래밍을 구현하는 지 살펴보았다. 이어서는 이어지는 글에서는 클래스 없이 Javascript에서 객체를 생성하고자 했던 다양한 시도와 방법들을 소개하려고 한다.
'Language > Javascript' 카테고리의 다른 글
유용하지만 위험한 화살표함수(=>) (0) | 2021.01.16 |
---|---|
OOP (3) Javascript에서의 객체지향 프로그래밍 (ES6 이전의 Pseudoclassical한 방법) (0) | 2021.01.16 |
OOP (1) 객체지향 프로그래밍, 클래스 (0) | 2021.01.14 |
this(1) 상황따라 바뀌는 this (0) | 2021.01.14 |
실행 컨텍스트(Execution Context) (0) | 2021.01.13 |
댓글