Prototype Chain

Minjae Kwon·2020년 11월 1일
0

 🍉   Learning Journal

목록 보기
23/36
post-thumbnail
post-custom-banner

자바스크립트는 prototype 기반의 언어이다.

🙋🏻‍♀️ 프로토타입이 뭐예요?

자바스크립트 코드의 대부분은 객체 구조를 띠고 있다. 이 객체들은 Object 라고 하는 대빵 생성자 함수의 instance 객체들이다. 함수, 배열, 우리가 만드는 객체 하나 하나들이 다 이 대빵 Object 생성자 함수instance 객체 이다.

모든 함수는 생성되는 순간 prototype 이라고 불리우는 객체를 속성으로 갖는다. (단, 화살표 함수는 이 prototype 객체가 생기지 않는다. ) 생성자 함수 역시 Object.prototype 이라는 원형 객체를 가지고 있으며, 이 prototype 은 해당 객체가 가지는 모든 메소드 함수들을 담고 있다. 우리가 아무 메소드도 정의하지 않는다면, 항상 디폴트로 가지는 것은 2 가지. constructor__proto__ 이다.

생성자 함수의 경우, constructor 는 자기 자신이 되고, 인스턴스 객체의 경우 constructor 는 자신의 생성자 함수를 바라본다. 그리고 인스턴스 객체들은 __proto__ 라는 속성을 통해 자신의 생성자 함수의 prototype 객체로 연결된다.

위의 예시에서 보면,

1) Object 라는 대빵 함수가 존재하고,
2) 내가 만든 임의의 greetings 라는 객체는
3) __proto__ 를 통해 대빵 함수의 prototype 에 연결되었다.

🙋🏻‍♀️ 그럼 프로토타입 체인은 뭘까요?

프로토타입 체인이란, 그 이름에서 짐작할 수 있듯이 프로토타입들이 줄줄이 엮여있다는 뜻이다.

위의 예시에서, 나는 greetings 라는 객체에 hasOwnProperty 라는 메소드를 할당한 적이 없지만, 그 메소드를 사용할 수 있었다. 어떻게?

내가 객체의 어떤 속성에 접근하고자 할 때, 자바스크립트는 그 속성이 객체 자체 내에 있는지 (즉, own property로 있는지) 먼저 탐색한다. 없다면, __proto__ 를 타고 올라가서 생성자 함수의 prototype 내에 그 속성이 있는지 탐색한다. 위의 예시의 경우, greetings 는 hasOwnProperty를 정의한 적 없지만, Object.prototype 은 Object.prototype.hasOwnProperty 라는 속성을 갖고 있으므로 우리는 답을 얻을 수 있었다.

자바스크립트는 우리가 원하는 속성이 있는지 __proto__ 를 타고 올라가면서 상위 객체들의 prototype 을 모두 탐색하며, 만약 최종적으로 null 을 만나서 탐색이 종료될 때까지 그 속성을 찾지 못한다면, undefined 를 반환한다.

🙋🏻‍♀️ 예시를 좀 더 보여주세요!

아래의 경우를 보자.

const iAmFunc = function() {
	this.color = "brown";
  	this.habitat = "mountain"; 
}

const iAmObj = new iAmFunc(); 
const iAmObj2 = new iAmFunc(); 
console.log(iAmObj); // {color: "brown", habitat: "mountain"}
console.log(iAmObj2); // {color: "brown", habitat: "mountain"}

iAmFunc 라는 생성자 함수를 통해 만들어진 인스턴스 객체 iAmObj, iAmObj2 는 생성자함수의 속성인 color, habitat 를 가지고 태어난다. 즉 속성을 상속받은 것이다.

만약 우리가 생성자 함수에 어떤 메소드를 추가한다면, 같은 생성자 함수를 통해 만들어진 모든 instance 들은 (생성자 함수의 prototype 에 연결되어있으므로) 생성자 함수의 메소드를 다같이 공유하게 된다. 단, 반드시 생성자함수의 prototype 에 추가해야 한다. 그래야만 인스턴스 객체들 역시 그 추가된 속성을 바로 사용할 수 있다.

// 프로토타입 체인을 통한 탐색 과정
iAmObj.__proto__ === iAmFunc.prototype
iAmObj.__proto__.__proto__ === Object.prototype
iAmObj.__proto__.__proto__.__proto__ === null 
// null 은 정의상 __proto__가 없으므로 여기가 프로토타입의 끝이다. 

// 아래와 같이 생성자 함수와 인스턴스 객체 각각에 name 이라는 속성을 추가해보자.
iAmFunc.prototype.name = "bear"; 
iAmObj.name = "rabbit"

console.log(iAmObj.name)  // "rabbit" 
// name 이라는 자체 속성이 있는가? 그렇다. 답은 "rabbit".

console.log(iAmObj2.name) // "bear". 
// name 이라는 자체 속성이 있는가? 아니오. 
// 그렇다면 상위 prototype 객체에 name 이라는 속성이 있는가? 그렇다. 답은 "bear".

console.log(iAmObj2.hasBaby) // undefined. 
// "hasBaby" 라는 자체 속성이 있는가? 아니오. 
// 그렇다면 상위 prototype 객체에 있는가? 아니오. 
// 쭉쭉 타고 올라가서 모든 prototype 체인을 검사하고도 못 찾았으므로 undefined.

iAmObj 의 상위 프로토타입 객체도 name 이라는 속성을 가지고 있었지만, iAmObj 객체 내에 자체적으로 name 이라는 속성을 갖고 있었으므로 prototype 체인 검사를 할 필요가 없었다. 이를 prototype shadowing 이라고 한다. 이 프로토타입 쉐도잉이 함수인 경우에는 method overriding 이라고 불리운다. 아래 예제를 보자.

자바스크립트에서는 어떤 함수든, 객체의 속성값으로 부여될 수 있으며 다른 속성값들과 동일하게 다뤄진다.

var iAmTheBoss = {
  num: 7,
  tool: function() {
  	return this.num + 3
  }
}; 

console.log(iAmTheBoss.tool()) // 10; 

// 위의 객체를 복사한 새로운 객체가 생겼다.
var hardWorker = Object.create(iAmTheBoss); 

console.log(hardWorker); // {}
console.log(hardWorker.__proto__) // { num: 7, tool: function() ... }
console.log(hardWorker.num) // 7; 
console.log(hardWorker.tool()) // 10; 

// 빈 객체였던 hardWorker 에 num 이라는 자체 속성을 부여했다. 
hardWorker.num = 0; 
console.log(hardWorker.tool()); // 3; 

hardWorker 객체는 바로 생성되었을 때 빈 객체이고, __proto__ 를 통해 iAmTheBoss 객체를 바라보고 있으므로 그 안에 정의된 속성들을 사용할 수 있다. hardWorker 내에 자체 num 속성이 생기자, tool 메소드를 사용할 때 상위 객체를 탐색할 필요가 없어졌는데, 이렇게 자체적으로 가진 속성값이 우선하는 경우를 두고, 메소드 오버라이딩이라고 한다.

💡 참고

메소드 오버라이딩(overriding), 오버로딩(overloading) 의 차이

우선 javascript 는 오버로딩을 지원하지 않는다. 주로 java 언어에서 이 두차이를 많이 비교한다. (관련 Stackoverflow 답변)

오버로딩은 파라미터만 다르게 해서 같은 이름의 함수를 여러 번 정의할 수 있는 특징이며, 오버라이딩은 하위클래스에서 메소드를 '재정의' 하여 그것을 우선으로 사용하는 특징이다. super class 의 메소드는 위의 예시에서처럼 경우에 따라 사용할 수도 있고, 안 할 수도 있다.

🙋🏻‍♀️ 아까 함수, 배열도 다 객체라고 하셨나요?

그렇다. 모두 Object.prototype 의 인스턴스 객체들이며, Array.prototype, Function.prototype 에 정의된 메소드들이 있기 때문에 우리가 만드는 배열, 함수에서 그 내장 메소드들을 상속받아 사용해 올 수 있었던 것이다.

var obj = { a: ‘apple’ }
// obj —> Object.prototype —> null 

var arr = [‘banana’, ‘cinnamon’ ]
// arr —> Array.prototype —> Object.prototype —> null

function func() { return ‘yo’ }
// func —> Function.prototype —> Object.prototype —> null

프로토타입 체인에 대한 MDN 자료가 궁금하다면 👉🏻 read more
profile
Front-end Developer. 자바스크립트 파헤치기에 주력하고 있습니다 🌴
post-custom-banner

0개의 댓글