JavaScript - 자바스크립트 프로토타입(prototype)
🚀 자바스크립트의 프로토타입
기존의 C++이나 JAVA 등과는 다르게 자바스크립트는 프로토타입
을 기반으로 한 객체지향 프로그래밍을 지원합니다. 생성자 함수, 프르토타입, 클로저 등을 활용해 객체 지향 프로그래밍을 구현합니다.
물론 ES6 이후부터는 자바스크립트도 class 표현식
을 지원하여, 클래스 기반 언어와 유사하면서도 직관적으로 사용할 수 있게 되었습니다. 그러나 ES6의 class도 어디까지나 자바스크립트의 함수이며, 내부적으로는 프로토타입 모델을 따르고 있습니다. 그래서 자바스크립트는 여전히 프로토타입을 기반으로 한 객체지향 프로그래밍
을 지원한다고 말할 수 있습니다.
⭐ 자바스크립트의 모든 객체는 자기의 부모 역할을 하는 프로토타입 객체
를 가진다
자신의 프로토타입
을 가리키는 [[Prototype]]
이라는 내부 프로퍼티를 기본으로 가집니다. 크롬 브라우저에서는 __proto__
라고 표현되어 있는데, 같은 것입니다. 이것은 객체의 부모를 가리키는 역할을 합니다.
정말로 그런지 확인해 보겠습니다.
함수, 배열, 그리고 일반 객체(모두 각각의 리터럴로 생성)에 대하여 console.dir을 크롬 브라우저에서 출력해 보았습니다.
1
2
3
4
5
6
7
8
9
10
function func(){
};
console.dir(func);
var arr = [ ];
console.dir(arr);
var obj = { };
console.dir(obj);
이들 모두가 자바스크립트에서는 객체
입니다. 따라서, __proto__
프로퍼티를 공통으로 가지고 있음을 확인할 수 있습니다. 가리키는 값들은 약간씩 다릅니다.
- 함수 객체의 부모 역할을 하는 프로토타입 객체(
__proto__
)는 크롬 브라우저에서는 Empty 함수로 표현하고 있습니다. ECMA에서는 이것을 Function.prototype 객체라고 부릅니다. 그런데 그 위에prototype
이라는 프로퍼티도 있습니다. 이것은 함수 객체에 나타나는 특징인데, 아래쪽에서 살펴볼 예정입니다. - 배열 객체의 부모 역할을 하는 프로토타입 객체(
__proto__
)는 크롬 브라우저에서는Array[0]
으로 표현하고 있습니다. ECMA에서는 이것을 Array.prototype 객체라고 부릅니다. 자바스크립트에서 배열을 선언하고 push, find, slice 등의 메서드를 사용할 수 있던 이유는 Array.prototype 객체(크롬에서Array[0]
객체)에 정의되어 있기 때문입니다. 이는 뒤에서 좀 더 자세히 말씀드리도록 하겠습니다. - 일반 객체의 부모 역할을 하는 프로토타입 객체(
__proto__
)는 크롬 브라우저에서 Object로 표현하고 있으며, ECMA에서는 이것을 Object.prototype 객체라고 부릅니다. (이 객체는 생성자 함수로 생성한 객체가 아님) - 사실 모든 객체들은 타고 올라가 보면 Object.prototype 이라는 공통 조상을 가지고 있는 셈입니다.
이처럼 모든 객체들은 자신의 부모 역할인 프로토타입 객체를 가지고 있음을 확인할 수 있습니다.
📫 자바스크립트의 생성자 함수 & 함수의 프로토타입 객체
우선, 기본적으로 함수를 정의하면 그 함수
와 함께 함수의 프로토타입 객체
가 같이 생성됩니다. 예를 들어,
1
2
3
function Person() {
}
으로 Person 함수를 선언했을 경우, Person.prototype 객체 도 함께 생성됩니다.
즉, 자바스크립트에서는 함수를 선언하면, 함수 Prototype 객체도 같이 만들어지는구나! 라고 이해하시면 됩니다.
주의!
: 함수의__proto__
가 가리키는 객체와는 다른 것이니 유의해 주세요.
이렇게 만들어진 Person.prototype 객체는 constructor
프로퍼티를 가지는데, 이것은 Person()을 가리킵니다. 함수가 정의될 때, 기본적으로 함수는 생성자 자격
이 부여된다는 의미와 같습니다.
또한 Person 함수는 prototype 프로퍼티를 가지며, 이것은 Person.prototype 객체를 가리킵니다.
말로만 되어 있어서 헷갈리실 수 있습니다. 아래쪽에서 그림과 함께 살펴볼 것입니다.
자바스크립트에서 일반적으로 객체를 생성하는 방법은 다음과 같습니다.
객체 리터럴
방식으로 생성 ({ }
)
1
2
3
var foo = {
name : 'chan';
}
- 생성자 함수로 생성
1
2
3
4
5
function Person(name){ // Person() 생성자 함수
this.name = name;
// 생성자 함수에서는 별도의 리턴값이 없으면, this로 바인딩된 새로 생성된 객체가 반환된다.
}
var foo = new Person('chan'); // Person() 생성자 함수로 foo 객체 생성
생성자 함수라는 것은 특이한 함수가 아닙니다. 기존의 함수를 new 키워드와 같이 사용하면 그 함수는 생성자의 역할을 합니다. 위쪽에서 살펴보고 왔듯이 함수는 태생에 constructor 역할이 주어지기 때문입니다. 여기서, 생성자 함수로 만든 foo 객체를 살펴볼 필요가 있습니다.
위의 코드처럼 생성자 함수로 객체를 생성했을 때의 상황은 아래와 같습니다. 일단 아래 나온 것들은 모두 객체
이므로, __proto__
를 기본으로 가집니다. 여기서는 Person() 생성자 함수의 Person.prototype 객체와, foo 객체의 __proto__
를 눈여겨 보아야 합니다. foo의 부모는 과연 누구인가? 하는 것입니다.
- Person() 생성자 함수를 정의하고 foo 객체를 생성한다. (이 때, Person.prototype 객체도 생성되었다.)
- Person() 생성자 함수는 prototype 프로퍼티로 Person.prototype 객체를 가리킵니다.
- 생성된 foo 객체는 Person() 함수의 프로토타입 객체인 Person.prototype 객체를 부모로 가집니다. 즉, foo 객체의
__proto__
프로퍼티는 Person() 생성자 함수를 가리키는 것이 아니라, Person.prototype 객체를 가리킵니다! - 따라서, Person() 생성자 함수의
prototpye
프로퍼티와, foo 객체의__proto__
프로퍼티는 서로 같은 것을 가리키고 있는 것입니다.
🛸 요약해 보면 다음과 같습니다.
- 모든 객체는, 자신의 부모 역할을 하는 프로토타입 객체를 가리키는
__proto__
프로퍼티를 기본으로 가진다. - 함수의 경우, 함수의 프로토타입 객체를 가리키는
prototype
프로퍼티가 별도로 존재한다.- 함수 자체의
__proto__
프로퍼티가 가리키는 Function.prototype (함수의 length, arguments, length등의 기본 메서드가 정의되어 있음) 과는 다른 것입니다. - 예를 들어 Person 함수를 선언하면, Person 함수의
prototype
프로퍼티는 Person.prototype 객체를 가리키고, Child 함수를 선언하면, Child 함수의prototype
프로퍼티는 Child.prototype 객체를 가리킬 것입니다.
- 함수 자체의
- 생성자 함수로 만든 (일종의 자식) 객체의
__proto__
프로퍼티는 그 생성자 함수의 프로토타입 객체를 가리킨다. 즉, 생성자 함수의prototype
프로퍼티가 가리키는 프로토타입 객체를 가리킨다.
생성자 함수로 생성한 객체가 가리키는 함수의 프로토타입 객체, 그리고 이러한 __proto__
속성들이 어떻게 사용되는지, 프로토타입 체이닝
이라는 주제로 다음 포스팅에서 살펴보도록 하겠습니다.