책 읽다가 코딩하다 죽을래

prototype chaining에 대해 알아봅시다 본문

코딩/자바스크립트

prototype chaining에 대해 알아봅시다

ABlue 2021. 10. 5. 21:15

 

🧾 이 강의는 시리즈 별로 되어있습니다.

 

이 강의를 읽기 전에 확인하세요!

자바스크립트 prototype에 대해 지금 같이 알아볼까?[클릭]

prototype으로 배워보는 메소드 상속 및 동작 원리[클릭]

 

 

 

 

 

prototype chaining에 대해 알아보기 위해선 구글링 해서 개념을 이해하기보다는 실제로 사용해서 알아보는 것이 나을 것이다. 

📚 prototype chaining이란?

 

 

 

ctrl + shift + j  눌러 크롬 개발자 도구 콘솔 창을 열어 다음과 같이 입력해보자

배열 리터럴 [1,2,3]의 prototype을 알아보기 위한 코드이다.

 

다음과 같은 출력이 나오는데 출력 코드를 한 번 더 클릭하여 펼쳐보자

 

 

클릭하면 [1,2,3]에 대한 정보가 나와있는데 [1,2,3]에 생성자가 array라는 것이 보여있다.

 

 

즉 이런 식이다. 배열 리터럴 [1,2,3]은 Object.getPrototype([1,2,3])을 입력할 때 임시적으로 array 생성자를 통해 생성되고 array에 대한 속성과 메소드들을 사용할 수 있는 것이다.

 

그런데 배열에는 getPrototype이 없는데 어떻게 사용하는건가요?

이 물음에 대답하려면

 

 

지난 시간에 인스턴스의 [[prototype]]생성자의 prototype과 같다고 했었습니다.

맨 밑에 [1,2,3]의 [[prototype]]을 눌러보면 Object의 관한 메소드들이 나와있는 걸 알 수 있습니다.

 

즉 이런 식으로 되어있다는 것입니다.

[1,2,3]은 array를 상속받고 array는 Object의 내용들을 상속받습니다. 마치 할아버지가 아버지에게 유전자를 물려주고 아버지는 자식에게 유전자를 물려주는 것처럼 말이죠.

 

여기서 object의 생성자를 보면 우리가 아까 사용했던 getPrototypeOf 함수가 있습니다.

 

 

그렇습니다 우리는 이런 식으로 접근하여 getPrototypeOf()를 사용할 수 있었던 것입니다.

이렇게 하위 클래스의 prototype과 상위 클래스의 prototype 연결되는 구조가 마치 체인을 서로 연결한 것처럼 보여서 prototype chaining이라 부릅니다.

 

prototype chaining에는 몇 가지 특징이 따릅니다.

 

 

📋 prototype chaining의 특징

 

1. 모든 데이터 타입의 상위 타입은 Object이다.

 

prototype은 객체이므로 맨 위의 상위 데이터 타입인 Object와 연결되어 있습니다. 그래서 모든 데이터 타입은 위 사진과 같이 동일한 구조를 따릅니다. 문자열이든, 숫자든, 함수든 간에 Object.prototype과 prototype chaining과 연결되어 있습니다.

 

그래서 어떤 데이터 타입이든 Object의 hasOwnProperty(), toString()등의 Object 메소드를 사용할 수 있는 것입니다.

모두 Object prototype과 연결되어 있으니까요.

 

 

2. 객체 prototype에는 객체 전용 메소드를 정의할 수 없습니다.

 

그 이유는 객체의 프로토타입에 있는 메소드는 모든 데이터 타입에 적용되기 때문입니다.

만약 객체의 value값을 출력해주는 values()라는 함수를 정의했다고 가정해봅시다.

 

이런 식으로 Object prototype의 values()가 생성될 것이고

객체 데이터 타입을 가진 객체에게 메소드를 부여해주면 값이 정상적으로 출력이 될 것입니다.

 

하지만 Date, number, boolean 타입에도 prototype chaining을 타고 Object의 values 메소드에 접근할 수 있게 됩니다. 하지만 제대로 된 값을 불러낼 수 없겠죠. 이를 막기 위해서 객체 prototype에는 객체 전용 메소드를 정의할 수 없습니다.

 

그래서 객체 prototype이 아닌 객체 생성자에 직접 객체 전용 메소드를 정의해야 합니다.

 

 

그래서 객체 전용 메소드들은 Object.메소드(obj)의 방식으로 호출하면서 매개변수로 객체 자신을 넘겨주는 방식을 지향하고 있습니다. 그 이유는 바로 prototype chaining 때문입니다.

 

 

3. 메소드를 불러올 때 자신과 가까운 prototype에 있는 메소드를 우선순위로 부른다.

 

배열[1,2,3]에 toString() 메소드를 붙여 배열 원소들을 출력해봅시다.

 

배열 원소 값이 string 타입으로 예쁘게 잘 출력된다.

 

[1,2,3].toString() 함수를 사용했을 때 toString()는 Array.prototype에 정의된 toString()을 사용하게 된다.

마치 위 사진과 같을 것이다.

 

만약 Array prototype에 정의된 toString()함수를 지운다면 어떻게 될까요?

 

이번에는 전혀 예쁘지 않은 문자열로 출력이 됩니다.

 

 

이와 같은 일이 벌어지는 이유는 이번에는 Object.prototype.toString()가 호출되었기 때문입니다.

Array.prototype.toString() 이 사라졌기 때문에 prototype chaining을 타고 Object.prototype에 정의된 toString()를 출력하게 된 것입니다. 

 

🤔 잠만요 Object.prototype.toString()이 출력된 게 맞나요?

 

네 맞습니다

🤔 그러면 Array.prototype.toString()은 놔두고 Object.prototype.toString()을 없애면요?

 

Array.prototype.toString()이 호출됩니다.
Obejct.prototype.toString()은 없어진 거 확실하고요!

 

 

🤔 둘 다 없애면요?

prototype chaining을 타도 toString함수를 찾을 수 없어 오류가 납니다.

 

이를 통해서 알 수 있었던 건 메소드를 출력할 때 자신과 가까운 prototype에 있는 메소드를 우선순위로 출력한다는 것입니다.

 

 

처음에는 Array.prototype에서 toString()를 찾았지만

 

Array.prototype.toString()이 없어진 후에는 prototype chaining을 타서 한 단계 위인 Object.prorotype에서 toString()을 찾게 된 것입니다.

 

두 개 다 없어진 이후로는 에러를 발생시킬 수밖에 없습니다. 물론 지금 이 경우는 모든 toString()을 고의적으로 지우게 된 것이고요.

 

 

원래대로라면 이런 상태가 맞는 겁니다.

보통 평상시에는 Object.prototype까지 prototype chaining을 타지 않습니다.

'가장 먼저 발견된 메서드'를 실행하게 됩니다.

 

이는 스코프 체인과 개념이 거의 흡사합니다.

가장 먼저 발견된 변수, 함수부터 찾고,

찾지 못하면 더 넓은 범위로부터 찾는 것처럼 말이죠.

 

프로토타입 체인은 이 정도만 이해하셔도 충분합니다.

📝 prototype 정리

 

prototype : 인스턴스에는 메서드가 없음에도 불구하고 [[prototype]]이라는 매개체 덕분에 생성자 함수의 prorotype에 있는 메소드를 마치 자신의 것으로 쓸 수 있다.

 

prototype chaining : 그리고 빨간 점선들, 즉 [[prototype]]으로 이어진 각 prorotype들에 모두 접근할 수 있는 것들을 일컫어 prototype chaining이라 말한다.