프로토 타입 (Prototype)
자바스크립트는 클래스 기반 언어와 달리, 프로토타입 기반 언어이다.
자바스크립트의 모든 객체(Object)는 자신의 부모 역할을 하는 개체와 연결되어 있는데, 이 부모 객체를 프로토타입(Prototype) 이라고 부른다.
쉽게, 자식(객체)이 어떤 기능을 가지고 있지 않다면, 부모(프로토타입)에 가서 이 기능 있는지 확인 후 가져온다고 생각하면 될 듯하다.
생성자 함수를 통해 이해하기
먼저 Person 이라는 사람을 만드는 함수를 만든다고 가정해보자.
// 생성자 함수 (사람을 만드는 틀)
function Person(name, age) {
this.name = name;
this.age = age;
}
- 이때, Person 함수가 만들어질 때, Person.prototype이라는 공유 공간도 함께 생성된다.
- [[Prototype]]이라는 문법? 도 보게 될 텐데 쉽게 new Person()로 생성자 호출할 때 새로 만들어지는 객체의 [[Prototype]]이 Person.prototype으로 설정된다고 이해하시면 될 것 같다.
그럼 이제 모든 사람이 공통적으로 사용하는 기능(ex: 인사하기)을 메모리 절약을 위해 공유 공간에 넣어둬보자.
Person.prototype.hello = function() {
console.log("Hello");
};
그 다음 new 키워드를 사용해 실제 사람(객체)를 만들어보자.
const yeon = new Person("연벅", 25);
const bug = new Person("벅연", 30);
// yeon과 bug은 'hello' 함수가 없다. 하지만 실행은 잘 된다.
yeon.hello(); // Hello
bug.hello(); // Hello

프로토타입 체인(Prototype Chain)
만약 Person.prototype에도 찾는 기능이 없다면 어떻게 될까?
자바스크립트에서는 그 위 부모를 또 찾아간다. 이것이 사슬 같다고 해서 프로토타입 체인이라고 부른다.
console.log(yeon.toString());
//결과: [object Object]
- yeon 객체에서 toString을 먼저 찾는다 → 없음
- yeon의 프로토타입(Person.prototype)으로 가서 찾음 → 없음
- Person.prototype의 부모인 Object.prototype(자바스크립트의 모든 객체의 조상)으로 가서 찾음 → 있음(실행)
만약 Object.prototype까지 없으면 null(체인의 끝)까지 가고 null 까지 찾아도 없으면 undefined를 반환하거나 에러를 낸다.
__proto__ 과 Object.getPrototypeof()
__proto__ 는 Object.prototype에 있는 getter / setter (접근자) 프로퍼티이다.
쉽게 말해, 객체와 그 부모(프로토타입)를 연결하는 다리 or 링크라고 생각하면 된다.
const yeon = {};
console.log(yeon.__proto__ === Object.prototype); // 보통 true
이 __proto__는 예를 들어, yeon.__proto__ 라고 호출하면, 사실은 Object.prototype에 있는 함수가 실행되어 "자, 네 부모님(프로토타입)주소는 이거다" 라고 알려주는 방식
__proto__ 는 프로토타입을 확인하는 것 뿐만 아니라, obj.__proto__ = otherobj 처럼 할당해 프로토타입을 변경할 수도 있다.
Object.getPrototypeOf(yeon)는 객체 yeon의 프로토타입(즉, 내부 [[Prototype]] 값)을 반환하는 공식적이고 표준화된 메서드이다.
이 메서드는 값을 가져오기만 하기에 프로토타입을 설정하고 싶다면 Object.setPrototypeOf()라는 별도의 메서드를 사용해야 한다.
__proto__는 3가지 정도의 이유로 사용을 지양하고 있다.
- 성능 이슈
: 자바스크립트 엔진(V8 등)은 객체의 구조가 변하지 않을 것이라 가정하고 최적화를 수행하는데, __proto__ 를 사용해 실행 중에 상속 관계를 바꾸는 것은 엔진의 최적화를 깨뜨려 심각한 성능 저하를 일으킬 수 있다. - 순환 참조 오류
: __proto__는 순환 참조(A가 B를 상속 받는데, B가 다시 A를 상속받음)가 발생하면 에러를 던지지만, 코드의 흐름으로 예측하기 어렵게 만든다. - 객체 오염(Prototype Pollution)
: __proto__는 단순한 속성처럼 보이지만 Setter 기능이 있어, 공격자가 JSON 입력값 등을 조작해 모든 객체의 기본 동작을 망가뜨리는 공격 경로로 활용될 수 있다.
따라서 현대 자바스크립트에서는 다음과 같은 패턴을 권장한다고 한다.
객체를 생성할 때 프로토타입을 지정하려면 Object.create()를 사용
const yeon = Object.create(Person); // 처음부터 Person을 부모로 하여 생성
프로토타입을 확인하려면 Object.getPrototype()를 사용
const proto = Object.getPrototypeOf(yeon);
이미 생성된 객체의 프로토타입을 바꿔야 한다면 Object.setPrototypeOf()를 사용
Object.setPrototypeOf(yeon, otherPerson);
요약
__proto__는 과거의 유산이다. 브라우저 콘솔에서 디버깅할 때 확인 용도로만 살짝 쓰고, 실제 코드(Production)에는 작성하지 않는 것이 좋다.
대신 표준 메서드인 Object.getPrototypeOf()를 사용하는 것이 훨씬 안전하고 전문적인 방법이다.