자바스크립트는 프로토타입 기반 언어. 클래스 기반언어와는 달리 특정 객체를 원형으로 삼고 이를 복제하여 사용한다.
생성자 함수를 통해 생성되는 인스턴스의 경우 __proto__라는 프로퍼티를 부여받고,
__proto__ 프로퍼티는 원래 생성자 함수의 prototype 이라는 프로퍼티를 참조한다.
하지만 자바스크립트의 메서드는 바로 앞의 객체를 참조하기 때문에, __proto__를 생략할 수 있는 규칙이 존재한다.
따라서 prototype 속성을 참조하는 __proto__는 생략 가능하기 때문에,
인스턴스에서도 해당 속성을 생략하여 prototype에 존재하는 메서드 혹은 속성에 접근 가능하다.
class User {
constructor() {
this.name = "KIM"
}
}
User.prototype.getName = function () {
console.log(this.name)
}
const user = new User()
user.__proto__.getName() // undefined, __proto__ 는 생략 가능하다. 여기서 getName은 user가 아닌 user.__proto__ 라는 객체를 가리킨다.
user.getName() // "KIM", user(.__proto__).getName()
console.log(User.prototype === user.__proto__) // true, 참조하는 관계이므로 동일한 객체
constructor라는 속성을 통해 원래의 생성자 함수를 참조할 수 있다.
즉 이 속성을 통해 인스턴스에서도 __proto__를 생략하여 원형을 참고할 수 있다.
let arr = [1, 2]
console.log(Array.prototype.constructor === arr.__proto__.constructor) // true
console.log(Array.prototype.constructor === arr.constructor) // true
let arr2 = arr.constructor(3, 4)
console.log(Array.prototype.constructor == arr2.constructor) // true
인스턴스가 prototype에 정의된 속성 혹은 메서드를 가지고 있다면, 메서드 오버라이딩이 발생한다.
자바스크립트 엔진이 객체의 메서드를 찾는 순서는 가장 가까운 속성부터 다음 속성을 찾아가기 때문이다.
이는 prototype 자체의 메서드를 변경한것이 아니며, 해당 인스턴스의 메서드가 추가된 것이다.
실제로 콘솔로 확인할 시 __proto__는 동일하며 동일한 이름의 메서드가 객체 내에 추가되어있다.
자신과 가장 가까운 속성부터 찾는 자바스크립트는 __proto__가 아닌 자기 자신 this에서 오버라이딩 된 메서드를 찾는다.
class User {
constructor(name) {
this.name = name;
}
getName() {
console.log(this.name)
}
}
let lee = new User("Lee")
lee.getName = function () { // 동일한 이름의 메서드가 객체에 추가.
console.log("Kim")
}
lee.getName() // "Kim"
__proto__ 속성을 연속으로 생략한 것을 프로토타입 체인, 이 체인을 따라가는것을 프로토타입 체이닝 이라고 한다.
각 생성자 함수의 prototype 속성과 계속해서 연결되고 있으며, 이를 통해 원본 인스턴스에 접근 가능하다.
이 덕분에 최상단의 Object.prototype에 존재하는 범용적인 메서드들, toString, hasOwnProperty 등을 여러 객체에서 사용할 수 있다.