책을 읽은 지는 꽤 지났지만, 다시 한번 복습 겸 기록을 남겨보려고 합니다. 책을 읽고 요약해 정리한 내용의 전문은 제 개인 노션에 정리해 놓았습니다.
이전 글 : [FRONT-END/JavaScript] - 05. 클로저 (코어 자바스크립트)
프로토타입
'자바스크립트는 프로토타입 기반 언어다.' 라는 말 들어보셨죠?
클래스 기반에서는 상속을 사용하지만, 프로토타입 기반 언어에서는 어떤 객체를 원형으로 삼고 이를 복제(참조)함으로써 상속과 비슷한 효과를 얻습니다.
프로토타입 이해하기
위 말이 지금은 이해가 되지 않으시더라도 해당 챕터를 마무리할 때는 이해가 되실 거예요! 저도 그랬으니까요!
책에서 나오는 기억해야 아래 프로토타입 도식을 잘 기억해 주세요!
var instance = new Constructor()
위 코드를 그림으로 그리면 위 도식이 됩니다.
어떤 생성자 함수를 new 연산자와 함께 호출하면, Construct에서 정의된 내용을 바탕으로 새로운 instance가 생성됩니다.
이때 instance에는 __proto__라는 프로퍼티가 자동으로 부여되는데, 이 프로퍼티는 Constructor의 prototype을 참조합니다.
prototype은 객체, 이를 참조하는 __proto__도 객체입니다.
prototype 객체 내부에서는 인스턴스가 사용할 메서드를 정의합니다.
+. 참고로 __proto__는 던더 프로토라고 발음하면 됩니다. double underscore의 줄임말이라고 합니다 :)
var Person = function (name) {
this._name = name
}
Person.prototype.getName = function() {
return this._name
}
var suzi = new Person('Suzi')
suzi.__proto__.getName() // undefined <- this 바인딩 대상이 잘못 지정됨
suzi.getName() // Suzi <- __proto__는 생략 가능한 프로퍼티
자바스크립트는 함수에 자동으로 객체인 prototype 프로퍼티를 생성해 놓는데, 해당 함수를 생성자 함수로 사용할 경우
즉, new 연산자와 함께 함수를 호출할 경우 그로부터 생성된 인스턴스에는 숨겨진 프로퍼티인 __proto__가 자동으로 생성됩니다.
__proto__ 프로퍼티는 생성자 함수의 prototype 프로퍼티를 참조합니다.
__proto__ 프로퍼티는 생략 가능하도록 구현되어 있기 때문에 생성자 함수의 prototype에 어떤 메서드나 프로퍼티가 있다면 인스턴스에서도 마치 자신의 것인 것처럼 해당 메서드나 프로퍼티에 접근할 수 있게 됩니다.
배열 리터럴과 Array의 관계를 통해 도식화를 살펴보겠습니다!
var arr = [1, 2]
console.dir(arr)
console.dir(Array)
constructor 프로퍼티
원래의 생성자 함수(자기 자신)를 참조합니다. 인스턴스로부터 그 원형이 무엇인지를 알 수 있는 수단입니다.
var arr = [1, 2]
Array.prototype.constructor === Array // true
arr.__proto__.constructor === Array // true
arr.constructor === Array // true
var arr2 = new arr.constructor(3, 4)
console.log(arr2) // [3, 4]
constructor는 읽기 전용 속성이 부여된 예외적인 경우(number, string, boolean)를 제외하고는 값을 바꿀 수 있습니다.
단, constructor를 변경하더라도 참조하는 대상이 변경될 뿐 이미 만들어진 인스턴스의 원형이 바뀌거나 데이터 타입이 변하진 않습니다.
즉 변경이 가능하기 때문에 어떤 인스턴스의 생성자 정보를 알아내기 위해 constructor 프로퍼티에 의존하는 게 항상 안전하지는 않습니다.
모두 동일한 대상을 가리키는 코드입니다.
[Constructor]
[instance].__proto__.constructor
[instance].constructor
Object.getPrototypeOf([instance]).constructor
[Constructor].prototype.constructor
모두 동일한 객체(prototype)에 접근할 수 있는 코드입닌다.
[Constructor].prototype
[instance].__proto__
[instance]
Object.getPrototypeOf([instance])
프로토타입 체인
인스턴스가 동일한 이름의 프로퍼티 또는 메서드를 가지고 있다면 메서드 오버라이드가 됩니다.
일반적으로 메서도가 오버라이드 된 경우 자신으로부터 가장 가까운 메서드에만 접근할 수 있지만, 다음으로 가까운 __proto__의 메서드도 우회적인 방법을 통해서지만 접근이 불가능한 것은 아닙니다.
어떤 데이터의 __proto__ 프로퍼티 내부에 다시 __proto__프로퍼티가 연쇄적으로 이어진 것을 프로토타입 체인이라고 하고, 이 체인을 따라가며 검색하는 것을 프로토타입 체이닝이라고 합니다.
위쪽 삼각형의 우측 꼭지점에는 무조건 Object.prototype이 있습니다. Object.create(null)은 __proto__가 없는 객체를 생성합니다.
삼각형은 여러 개 연결될 수 있습니다.
즉, 어떤 생성자 함수든 prototype은 반드시 객체이기 때문에 Object.prototype이 언제나 프로토타입 체인의 최상단에 존재하게 됩니다.
다중 프로토타입 체인
대각선의 __proto__를 연결해나가기만 하면 무한대로 체인 관계를 이어나갈 수 있습니다.
대각선의 __proto__를 연결하는 방법은 __proto__가 가리키는 대상, 즉 생성자 함수의 prototype이 연결하고자 하는 상위 생성자 함수의 인스턴스를 바라보게끔 해주면 됩니다.
var Grade = function () {
var args = Array.prototype.slice.call(arguments)
for (var i = 0; i < args.length; i++) {
this[i] = args[i]
}
this.length = args.length
}
var g = new Grade(100, 80)
인스턴스에 배열 메서드를 직접 쓸 수 있게끔 하기 위해서는 g.__proto__ 즉, Grade.prototype이 배열의 인스턴스를 바라보게 하면 됩니다.
Grade.prototype = []
위 코드를 통해 아래 그림 6-13처럼 한 단계 연결을 추가해 준 것을 확인할 수 있습니다.
g.pop()
console.log(g) // Grade(1) [100]
이제 위 코드가 가능해지게 됩니다!
다음 글 : [FRONT-END/JavaScript] - 07. 클래스 (코어 자바스크립트)
'FRONT-END > JavaScript' 카테고리의 다른 글
07. 클래스 (코어 자바스크립트) (1) | 2024.09.24 |
---|---|
05. 클로저 (코어 자바스크립트) (6) | 2024.09.22 |
04. 콜백 함수 (코어 자바스크립트) (1) | 2024.09.22 |
03. this (코어 자바스크립트) (1) | 2024.09.22 |
02. 실행 컨텍스트 (코어 자바스크립트) (0) | 2024.09.21 |
댓글