본문 바로가기
FRONT-END/JavaScript

06. 프로토타입 (코어 자바스크립트)

by 랄라J 2024. 9. 23.

책을 읽은 지는 꽤 지났지만, 다시 한번 복습 겸 기록을 남겨보려고 합니다. 책을 읽고 요약해 정리한 내용의 전문은 제 개인 노션에 정리해 놓았습니다.

 

이전 글 : [FRONT-END/JavaScript] - 05. 클로저 (코어 자바스크립트)

 

05. 클로저 (코어 자바스크립트)

책을 읽은 지는 꽤 지났지만, 다시 한번 복습 겸 기록을 남겨보려고 합니다. 책을 읽고 요약해 정리한 내용의 전문은 제 개인 노션에 정리해 놓았습니다. 이전 글 : [FRONT-END/JavaScript] - 04. 콜백

rarla-j.tistory.com

 

 

프로토타입

'자바스크립트는 프로토타입 기반 언어다.' 라는 말 들어보셨죠?

클래스 기반에서는 상속을 사용하지만, 프로토타입 기반 언어에서는 어떤 객체를 원형으로 삼고 이를 복제(참조)함으로써 상속과 비슷한 효과를 얻습니다.

 

프로토타입 이해하기

위 말이 지금은 이해가 되지 않으시더라도 해당 챕터를 마무리할 때는 이해가 되실 거예요! 저도 그랬으니까요!

책에서 나오는 기억해야 아래 프로토타입 도식을 잘 기억해 주세요!

코어 자바스크립트 내 그림 6-2 프로토타입 도식(2)

 

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)

코어 자바스크립트 내 그림 6-6 배열 리터럴과 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처럼 한 단계 연결을 추가해 준 것을 확인할 수 있습니다.

 

코어 자바스크립트 내 그림 6-13, 6-14

g.pop()
console.log(g) // Grade(1) [100]

이제 위 코드가 가능해지게 됩니다!

 

다음 글 : [FRONT-END/JavaScript] - 07. 클래스 (코어 자바스크립트)

 

07. 클래스 (코어 자바스크립트)

책을 읽은 지는 꽤 지났지만, 다시 한번 복습 겸 기록을 남겨보려고 합니다. 책을 읽고 요약해 정리한 내용의 전문은 제 개인 노션에 정리해 놓았습니다. 이전 글 : [FRONT-END/JavaScript] - 06. 프로토

rarla-j.tistory.com

 

728x90

댓글