본문 바로가기
웹(web)/프론트엔드-javascript

[Prototype] Prototype Chaining

by 바코94 2020. 6. 2.

js에서는 클래스 개념이 없고 리터럴 방식이나 생성자로 객체를 생성한다. 이 때 생성된 객체의 부모가 무엇인지 파악해보자.

 

생성자 함수로 객체를 생성하는 경우

객체의 부모는 생성자 함수의 prototype속성이 참조하는 객체이다. 앞서 생성자 함수는 constructor가 있는 객체를 가리킨다고 한 것을 기억할 것이다. 

 

리터럴로 객체를 생성하는 경우

var a = {} 와 같이 생성하면 내부적으로 Ojbect() 생성자 함수를 사용해 객체를 생성한다. 그리고 a의 부모는 Object 생성자 함수가 prototype속성으로 가리키는 객체이다. 모든 함수는 prototype속성이 있다.

a.hasOwnProperty('name');

이 때 위 코드를 실행할 수 있을까? 

답은 그렇다. 왜냐하면 a의 부모인 Object가 속성으로 hasOwnProperty를 가지고 있기 때문이다.

 

주의할 것은 모든 객체는 부모를 따라가서 마지막 부모는 Object.prototype 객체가 된다. 단, 생성자 함수로 객체를 생성할 때는 객체의 부모의 부모가 Object.prototype이 되고, 리터럴로 생성하면 부모가 Object.prototype이 된다.

 

앞서 구분했던 기본타입과 참조타입을 기억할 것이다. 참조 타입의 마지막 부모는 Object.prototype이다.

기본 타입도 마지막 부모는 Object.prototype이다. 기본타입은 부모가 정해져있다. 즉, 생성자 함수가 정해져있다는 뜻이다. string 객체를 생성할 때도 내부적으로 String() 생성자 함수를 사용한다. string 생성자 함수의 경우에는 prototype속성이 가리키는 객체가 String.prototype이다. 그래서 모든 string 객체는 String.prototype에 있는 속성들을 사용할 수 있다. 또, String.prototype의 부모는 Object.rptotype이다. 

 

정리하면 Object() 생성자를 사용하는 {} 와 같은 객체를 제외하고는 생성자 함수의 prototype속성이 가리키는 객체가 직계 부모가 된다. 이 prototype속성이 가리키는 객체의 부모는 모두 Oject.prototype을 부모로 갖는다.  생성자함수의 prototype속성이 가리키는 객체의 이름은 생성자함수이름.prototype이다. 예를들면, {}로 만들어서 Ojbect()로 만들어진 객체라면 Object.prototype이다. var a = "3" 과 같이 String()으로 만들어진 객체라면 String.prototype이다. 빈 객체가 아니라면 부모가 생성자함수이름.prototype 이라는 것과 그 부모는 Object.prototype이라는 것을 기억하자.

 

그렇다면 this 바인딩은 어떻게 될까? 다음 예시를 통해 이해해보자.

1. function A(name){ this.name = name;}

2. A.prototype.getName = function(){ return this.name};

3. var foo = new Person('foo');

4. A.prototype.name = 'bob';

 

위 네 줄의 코드를 이해해보자.

1. 함수 선언문으로 A를 만들었다. name을 인자로 받아서 바인딩 된 객체의 속성 name에 인자의 값을 저장한다. 

2. 생성자 함수의 prototype속성이 가리키는 객체인 A.prototype의 속성으로 함수를 추가했다. 이 속성을 사용하면 this.name을 리턴해준다. this는 함수를 호출한 객체이다. A()와 같이 호출하면 this는 window 객체이다.

3. 다음과 같은 구조로 foo가 만들어진다.

foo{name:'foo'} -->A.prototype{getName: 'return this.name' 코드를 가진 함수 객체의 참조값} ---> Object.prototype 

4. A.prototype 속성으로 name:'bob'이 추가된다. 

 

간단히 생각하면 foo객체가 만들어졌고  foo의 부모는 메서드 하나와 name:'bob'이 있는 객체이다. 물론, constructor 속성이 A생성자 함수를 가리킨다.

 

foo.getName();

이 코드를 실행하면 무슨 값이 리턴될까? 

우선 getName이 foo에 없으므로 부모로 간다. A.prototype에 getName이 있으므로 함수를 실행한다. getName을 실행할 때 this는 이 함수를 호출한 객체인 foo이다. 따라서 foo 객체의 name속성값인 'foo'이다. 따라서 'foo'가 리턴된다. 

 

A.prototype.getName();

이 코드를 실행해보자.

우선 A.prototype에 getName이 있으므로 함수를 실행한다. getName을 호출한 객체는 A.prototype이다. 따라서 this.name은 'bob'이다. 즉, 'bob'이 리턴된다.

 

생성자 함수의 prototype속성인 객체를 변경할 수 있다. prototype속성을 변경하는 코드 이후에서만 객체 생성시 부모로 매핑이 된다. prototype속성 변경 전에 객체를 생성했다면 부모 객체를 바꿔주어야 할 것이다.

'웹(web) > 프론트엔드-javascript' 카테고리의 다른 글

[Closure]  (0) 2020.06.03
[Execution Context] Function call  (0) 2020.06.03
[Function] return value  (0) 2020.06.01
[Function]this, apply, call  (0) 2020.06.01
[Function] varient using function  (0) 2020.06.01