C++과 Java는 클래스를 이용해서 객체를 생성하는 클래스 기반 객체 지향 언어이다.
반면, JavaScript는 클래스가 아닌 프로토타입을 상속하는 프로토타입 기반 객체지향 언어이다.
프로토 타입은 디자인 패턴이다.
프로토타입은 객체를 효율적으로 생성하는 방법을 다루는 패턴 중 하나인데,
주로 객체를 생성하는 비용이 클 때 이를 회피하기 위해 사용된다.
프로토타입은 원본 객체가 존재하고, 그 객체를 복제해서 새로운 객체를 생성하는 방법이다.
즉, 프로토타입 패턴이란 객체를 생성할 때 원본이 되는 객체를 복사해서 생성하는 패턴이라고 할 수 있다.
프로토타입을 지원하는 자바스크립트의 경우 모든 객체를 생성할 때 프로토타입을 사용하기 때문에
객체를 생성하기만 해도 프로토타입 패턴이 적용된다.
자바스크립트는 객체를 생성할 때 함수를 사용해서 생성한다.
console.log(Object);
console.log(typeof Object);
자바스크립트에서의 생성자는 클래스가 아니라 함수가 가지고 있다는 것을 확인할 수 있다.
만약 클래스기반 언어라면 Object는 클래스겠지만, 자바스크립트에서는 클래스가 아닌 함수이다.
- 프로토타입 패턴은 객체를 생성할 때 원본 객체를 복제하여 생성하는 방법이다.
- 자바스크립트는 객체를 생성할 때 프로토타입 패턴을 사용한다.
- 자바스크립트는 객체를 생성할 때 함수를 사용한다.
function Person() {}
const hee = new Person();
console.log(hee);
console.log(typeof hee);
자바스크립트는 함수를 사용해서 객체를 생성하기 때문에 객체와 유사한 느낌으로 객체 생성이 가능하다.
위의 예제에서 hee
객체는 Person
함수를 복제한 것이 아니라 Person
함수의 프로토타입 객체를 복제했다.
객체를 생성하면서 함수를 복제했다면, 생성된 객체는 object
타입이 아니라 function
이어야 하는데 hee
객체는 object
타입을 가지고 있다.
즉, 이 함수 자체가 아니라 다른 객체 타입의 무언가를 복제했다는 것이고,
그 원본 객체가 Person
함수의 프로토타입 객체인 것이다.
Person
함수의 프로토타입을 명시적으로 선언하지 않았지만,
자바스크립트는 함수가 생성될 때 자동으로 그 함수의 프로토타입 객체도 함께 생성하고 해당 함수의prototype
프로퍼티에 연결해둔다.
function Person() {}
console.log(Person.prototype);
console.log(typeof Person.prototype);
위 코드에서 확인할 수 있는 점은 함수 Person
만 선언했는데 console.log
로 Person.prototype
을 찍어보았을 때 프로퍼티에 뭔가 잔뜩 가지고 있는 객체가 같이 붙어서 나왔다.
여기서 알 수 있는 점은, 함수를 생성하면 무조건 그 함수의 프로토타입 객체도 함께 생성된다는 것이다.
그리고 이 프로토타입 객체는 함수를 사용해서 새로운 객체를 생성할 때 원본 객체 역할을 해줄 객체를 의미한다.
즉, new Person()
라는 문법을 사용해서 새로운 객체를 만들면 Person
함수 자체가 아니라 Person
함수가 생성될 때 함께 생성된 Person
함수의 프로토타입 객체를 복제해서 새로운 객체를 만든다.
여기서 그럼 constructor
과 __proto__
는 뭘까?
함수가 생성되며 생성된 프로토타입 객체는 모두 constructor
라는 프로퍼티를 가지고 있다.
그리고 이 프로퍼티에는 프로토타입 객체가 생성될 때 선언했던 함수가 들어있다.
함수를 선언하면 함수와 함께 해당 함수의 프로토타입 객체도 함께 생성되고 이 둘을 연결한다.
이 때 함수는 프로토타입 객체의 constructor
프로퍼티로 연결되고, 프로토타입 객체는 함수의 prototype
프로퍼티로 연결된다.
함수와 프로토타입 객체는 서로 연결되어있다.
함수를 통해 새롭게 생성된 객체는 원본 객체와의 연결을 가지고 있다.
이때 이 연결을 프로토타입 링크(Prototype Link)라고 한다.
자바스크립트에서 단순 원시 타입(simple primitive)인 문자열, 숫자, 불리언, null, undefined를 제외한 모든 타입은 객체다.
자바스크립트에서 객체는 원형 객체로부터 생성되며, 생성된 객체는 원형에 대한 프로토타입 링크 __proto__
를 갖게 된다.
Object.prototype
을 제외한 자바스크립트 내의 모든 객체는 원본 객체를 기반으로 복사되어 생성되었기 때문에, 자신의 원본 객체로 연결되어 있는 프로토타입 링크 또한 모든 객체가 가지고있다. 이 때 이 링크가 담기는 프로퍼티가 __proto__
프로퍼티이다.
즉, Person
함수를 사용하여 생성한 객체는 Person.prototype
객체를 복사하여 생성된 객체이므로, 이 객체들은 원본인 Person.prototype
객체를 자신의 __proto__
프로퍼티에 연결해두는 것이다.
Object.protype.__proto__
는 존재하지 않는다.
function Person() {}
const hee = new Person();
console.log(hee.__proto__ === Person.prototype);
String, Boolean, Array, ... 자바스크립트 내 존재하는 모든 객체는 바로 Object
함수의 프로토타입인 Object.prototype
을 시작으로 해서 복제된다.
위의 __proto__
설명에서 Object.protype.__proto__
는 존재하지 않는다고 했는데(프로토타입링크, 즉 원본 객체로 통하는 링크가 없다.) 그 이유는 바로 Object.prototype
이 모든 객체들의 조상이기 때문이다.
자바스크립트의 모든 함수는 자신의 원본으로 Function.prototype
객체를 원본으로 가진다.
그리고 Function.prototype
은 결국 객체이기 때문에, 당연히 원본으로 Object.prototype
객체를 원본으로 가진다.
만약 여기서 한번 더 올라간다면 TypeError
가 발생한다.
Object.prototype
객체의 원본 객체인 Object.prototype.__proto__
는 null
이기 때문이다.
즉, Object
의 위로는 조상이 없다.
이 관계를 다이어그램으로 살펴보면 위와 같다.
참고,인용한 글/출처
[JS 프로토타입] 자바스크립트의 프로토타입 훑어보기
프로토타입 기반 언어, 자바스크립트