상속과 프로토타입

  • 리비전 슬러그: JavaScript/Guide/Inheritance_and_the_prototype_chain
  • 리비전 제목: 상속과 프로토타입
  • 리비전 아이디: 316909
  • 제작일시:
  • 만든이: jaemin_jo
  • 현재 리비전인가요? 아니오
  • 댓글

리비전 내용

Java 나 C++ 같이 클래스를 기반으로한 언어를 사용하던 프로그래머들은 자바스크립트에서 클래스가 없다는 것을 이상하게 생각한다. 자바스크립트는 클래스를 지원하지 않는다. (하지만 class 키워드는 예약어이기 때문에 변수 이름으로 사용할 수 없다)

상속이라는 점에서 자바스크립트는 오브젝트라는 하나의 생성체를 가지고 있다. 모든 오브젝트는 프로토타입이라는 다른 오브젝트 (또는 null)를 가리키는 내부 링크를 가지고 있다. 한 오브젝트의 프로토타입 또한 프로토타입을 가지고 있고 이것이 반복되다 null 오브젝트를 프로토타입으로 가지는 오브젝트에서 끝난다. 이 오브젝트들의 연쇄를 프로토타입 체인이라고 부른다.

프로토타입 체인을 이용한 상속

속성 상속하기

자바스크립트 오브젝트는 속성을 저장하는 동적인 "가방"과 (자기만의 속성이라고 부른다) 프로토타입 오브젝트(또는  null)에 대한 링크를 가진다. 다음 예쩨에서 어떻게 오브젝트의 속성에 접근하는지 알아보자.

// 프로토타엡 체인이 다음과 같이 생겼다고 가정하자.
// {a:1, b:2} ---> {b:3, c:4} ---> null
// 'a' 와 'b' 은 o의 속성이다.

// 이 예제에서 오브젝트.[[Prototype]]은 오브젝트의 프로토타입을 의미한다고 생각하자.
// 이건 ECMAScript의 기본 표기법이고 스크립트 내부에서는 사용할 수 없다.

console.log(o.a); // 1
// o는 'a'라는 속성을 가지는가? 가지고 값은 1이다.

console.log(o.b); // 2
// o는 'b'라는 속성을 가지는가? 가지고 값은 2이다.
// 프로토타입 역시 'b'라는 속성을 가지지만 이 값은 쓰이지 않는다. 이것을 "속성의 가려짐(property shadowing)" 이라고 부른다.

console.log(o.c); // 4
// o는 'c'라는 속성을 가지는가? 아니다. 프로토타입을 확인해보자.
// o.[[Prototype]]은 'c'라는 속성을 가지는가? 가지고 값은 4이다.

console.log(o.d); // undefined
// o는 'd'라는 속성을 가지는가? 아니다. 프로토타입을 확인해보자.
// o.[[Prototype]]은 'd'라는 속성을 가지는가? 아니다. 다시 프로토타입을 확인해보자.
// o.[[Prototype]].[[Prototype]]은 null이다. 찾는 것을 그만두자. 속성이 발견되지 않았기 때문에 undefined를 반환한다.

오브젝트의 속성에 값을 지정하면 "자기만의 속성"이 생긴다.  단, getter or a setter로 상속된 속성에 대해서는 get과 set시 예외적인 동작을 보인다.

메쏘드 상속하기

자바스크립트에 "메쏘드"라는건 없다. 하지만 자바스크립트는 오브젝트의 속성으로 함수를 지정할 수 있고 속성 값을 사용하듯 쓸 수 있다. 속성 값으로 지정한 함수의 상속 역시 위에서 본 속성 값의 상속과 동일하다. (단 위에서 언급한 "속성의 가려짐" 대신 "메쏘드 오버라이딩, method overriding" 라는 용어를 사용한다)

함수가 오브젝트의 속성으로 지정되었을 때 하나의 다른점은 함수가 실행 될 때  this 라는  변수가 가리키는 값이다.

var o = {
  a: 2,
  m: function(b){
    return this.a + 1;
  }
};

console.log(o.m()); // 3
// o.m을 호출하면 'this' 는 o를 가리킨다.

var p = Object.create(o);
// p 는 프로토타입을 o로 가지는 오브젝트이다.

p.a = 12; // p 에 'a'라는 새로운 속성을 만들었다.
console.log(p.m()); // 13
// p.m이 호출 될 때 'this' 는 'p'를 가리킨다. 'this.a'는 p의 속성이다.

오브젝트를 생성하여 프로토타입을 만드는 다른 방법

문법 생성자로 오브젝트를 만들 때

var o = {a: 1};

// o 오브젝트는 프로토타입으로 Object.prototype 을 가진다.
// 이로 인해 o.hasOwnProperty('a') 같은 코드를 사용할 수 있다.
// hasOwnProperty 라는 속성은 Object.prototype 의 속성이다.
// Object.prototype 의 프로토타입은 null 이다.
// o ---> Object.prototype ---> null

var a = ["yo", "whadup", "?"];

// Array.prototype을 상속받은 배열도 마찬가지다. (이번에는 indexOf, forEach 등의 메소드를 가진다)
// 프로토타입 체인은 다음과 같다.
// a ---> Array.prototype ---> Object.prototype ---> null

function f(){
  return 2;
}

// 함수는 Function.prototype 을 상속받는다. (이 프로토타입은 call, bind 같은 메쏘드를 가진다)
// f ---> Function.prototype ---> Object.prototype ---> null

생성자를 사용해서

자바스크립트에서 생성자는 단지 new 연산자를  사용할 때 호출되는 함수이다.

function Graph() {
  this.vertexes = [];
  this.edges = [];
}

Graph.prototype = {
  addVertex: function(v){
    this.vertexes.push(v);
  }
};

var g = new Graph();
// g 'vertexes' 와 'edges'를 속성으로 가지는 오브젝트다.
// 생성시 g.[[Prototype]]은 Graph.prototype의 값과 같은 값을 가진다.

Object.create를 사용해서

ECMAScript 5는 새로운 방법을 도입했다. Object.create라는 메쏘드를 호출하여 새로운 오브젝트를 만들 수 있다. 생성된 오브젝트의 프로토타입은 이 메쏘드의 첫 번째 인수로 지정 다.

var a = {a: 1}; 
// a ---> Object.prototype ---> null

var b = Object.create(a);
// b ---> a ---> Object.prototype ---> null
console.log(b.a); // 1 (상속됨)

var c = Object.create(b);
// c ---> b ---> a ---> Object.prototype ---> null

var d = Object.create(null);
// d ---> null
console.log(d.hasOwnProperty); // undefined이다. 왜냐하면 d는 Object.prototype을 상속받지 않기 때문이다.

 

{{ languages( {"zh-cn": "zh-cn/JavaScript/Guide/Inheritance_and_the_prototype_chain" } ) }}

리비전 소스

<p>Java 나 C++ 같이 클래스를 기반으로한 언어를 사용하던 프로그래머들은 자바스크립트에서 클래스가 없다는 것을 이상하게 생각한다. 자바스크립트는 클래스를 지원하지 않는다. (하지만&nbsp;<code>class</code>&nbsp;키워드는 예약어이기 때문에 변수 이름으로 사용할 수 없다)</p>
<p>상속이라는 점에서 자바스크립트는 오브젝트라는 하나의 생성체를 가지고 있다. 모든 오브젝트는 <strong>프로토타입</strong>이라는 다른 오브젝트 (또는&nbsp;<code>null</code>)를 가리키는 내부 링크를 가지고 있다. 한 오브젝트의 프로토타입 또한 프로토타입을 가지고 있고 이것이 반복되다&nbsp;<span style="font-family: 'Courier New', 'Andale Mono', monospace; ">null 오브젝트를 프로토타입으로 가지는 오브젝트에서 끝난다. 이 오브젝트들의 연쇄를 <strong>프로토타입 체인</strong>이라고 부른다.</span></p>
<h2 id="Inheritance_with_the_prototype_chain">프로토타입 체인을 이용한 상속</h2>
<h3 id="Inheriting_properties">속성 상속하기</h3>
<p>자바스크립트 오브젝트는 속성을 저장하는 동적인 "가방"과 (<strong>자기만의 속성</strong>이라고 부른다) 프로토타입 오브젝트(또는 &nbsp;<span style="font-family: 'Courier New', 'Andale Mono', monospace; ">null)에 대한 링크를 가진다. 다음 예쩨에서 어떻게 오브젝트의 속성에 접근하는지 알아보자.</span></p>
<pre class="brush: js">
// 프로토타엡 체인이 다음과 같이 생겼다고 가정하자.
// {a:1, b:2} ---&gt; {b:3, c:4} ---&gt; null
// 'a' 와 'b' 은 o의 속성이다.

// 이 예제에서 오브젝트.[[Prototype]]은 오브젝트의 프로토타입을 의미한다고 생각하자.
// 이건 ECMAScript의 기본 표기법이고 스크립트 내부에서는 사용할 수 없다.

console.log(o.a); // 1
// o는 'a'라는 속성을 가지는가? 가지고 값은 1이다.

console.log(o.b); // 2
// o는 'b'라는 속성을 가지는가? 가지고 값은 2이다.
// 프로토타입 역시 'b'라는 속성을 가지지만 이 값은 쓰이지 않는다. 이것을 "속성의 가려짐(property shadowing)" 이라고 부른다.

console.log(o.c); // 4
// o는 'c'라는 속성을 가지는가? 아니다. 프로토타입을 확인해보자.
// o.[[Prototype]]은 'c'라는 속성을 가지는가? 가지고 값은 4이다.

console.log(o.d); // undefined
// o는 'd'라는 속성을 가지는가? 아니다. 프로토타입을 확인해보자.
// o.[[Prototype]]은 'd'라는 속성을 가지는가? 아니다. 다시 프로토타입을 확인해보자.
// o.[[Prototype]].[[Prototype]]은 null이다. 찾는 것을 그만두자. 속성이 발견되지 않았기 때문에 undefined를 반환한다.
</pre>
<p>오브젝트의 속성에 값을 지정하면 "자기만의 속성"이 생긴다. &nbsp;단,&nbsp;<a href="/en/JavaScript/Guide/Obsolete_Pages/Creating_New_Objects/Defining_Getters_and_Setters" title="Defining Getters and Setters">getter or a setter</a>로 상속된 속성에 대해서는 get과 set시 예외적인 동작을 보인다.</p>
<h3 id="Inheriting_&quot;methods&quot;">메쏘드 상속하기</h3>
<p>자바스크립트에 "메쏘드"라는건 없다. 하지만 자바스크립트는 오브젝트의 속성으로 함수를 지정할 수 있고 속성 값을 사용하듯 쓸 수 있다. 속성 값으로 지정한 함수의 상속 역시 위에서 본 속성 값의 상속과 동일하다. (단 위에서 언급한 "속성의 가려짐" 대신 "<em>메쏘드 오버라이딩,&nbsp;method overriding</em>" 라는 용어를 사용한다)</p>
<p>함수가 오브젝트의 속성으로 지정되었을 때 하나의 다른점은 함수가 실행 될 때 &nbsp;<a href="/en/JavaScript/Reference/Operators/this" title="this"><code>this</code></a>&nbsp;라는 &nbsp;변수가 가리키는 값이다.</p>
<pre class="brush: js">
var o = {
  a: 2,
  m: function(b){
    return this.a + 1;
  }
};

console.log(o.m()); // 3
// o.m을 호출하면 'this' 는 o를 가리킨다.

var p = Object.create(o);
// p 는 프로토타입을 o로 가지는 오브젝트이다.

p.a = 12; // p 에 'a'라는 새로운 속성을 만들었다.
console.log(p.m()); // 13
// p.m이 호출 될 때 'this' 는 'p'를 가리킨다. 'this.a'는 p의 속성이다.
</pre>
<h2 id="Different_ways_to_create_objects_and_the_resulting_prototype_chain">오브젝트를 생성하여 프로토타입을 만드는 다른 방법</h2>
<h3 id="Objects_created_with_syntax_constructs">문법 생성자로 오브젝트를 만들 때</h3>
<pre class="brush: js">
var o = {a: 1};

// o 오브젝트는 프로토타입으로 Object.prototype 을 가진다.
// 이로 인해 o.hasOwnProperty('a') 같은 코드를 사용할 수 있다.
// hasOwnProperty 라는 속성은 Object.prototype 의 속성이다.
// Object.prototype 의 프로토타입은 null 이다.
// o ---&gt; Object.prototype ---&gt; null

var a = ["yo", "whadup", "?"];

// Array.prototype을 상속받은 배열도 마찬가지다. (이번에는 indexOf, forEach 등의 메소드를 가진다)
// 프로토타입 체인은 다음과 같다.
// a ---&gt; Array.prototype ---&gt; Object.prototype ---&gt; null

function f(){
  return 2;
}

// 함수는 Function.prototype 을 상속받는다. (이 프로토타입은 call, bind 같은 메쏘드를 가진다)
// f ---&gt; Function.prototype ---&gt; Object.prototype ---&gt; null
</pre>
<h3 id="With_a_constructor">생성자를 사용해서</h3>
<p>자바스크립트에서 생성자는 단지&nbsp;<a href="/en/JavaScript/Reference/Operators/new" title="new">new 연산자</a>를 &nbsp;사용할 때 호출되는 함수이다.</p>
<pre class="brush: js">
function Graph() {
  this.vertexes = [];
  this.edges = [];
}

Graph.prototype = {
  addVertex: function(v){
    this.vertexes.push(v);
  }
};

var g = new Graph();
// g 'vertexes' 와 'edges'를 속성으로 가지는 오브젝트다.
// 생성시 g.[[Prototype]]은 Graph.prototype의 값과 같은 값을 가진다.
</pre>
<h3 id="With_Object.create">Object.create를 사용해서</h3>
<p>ECMAScript 5는 새로운 방법을 도입했다. <a href="/en/JavaScript/Reference/Global_Objects/Object/create" title="create">Object.create</a>라는 메쏘드를 호출하여 새로운 오브젝트를 만들 수 있다. 생성된 오브젝트의 프로토타입은 이 메쏘드의 첫 번째 인수로 지정 다.</p>
<pre class="brush: js">
var a = {a: 1}; 
// a ---&gt; Object.prototype ---&gt; null

var b = Object.create(a);
// b ---&gt; a ---&gt; Object.prototype ---&gt; null
console.log(b.a); // 1 (상속됨)

var c = Object.create(b);
// c ---&gt; b ---&gt; a ---&gt; Object.prototype ---&gt; null

var d = Object.create(null);
// d ---&gt; null
console.log(d.hasOwnProperty); // undefined이다. 왜냐하면 d는 Object.prototype을 상속받지 않기 때문이다.
</pre>
<p>&nbsp;</p>
<p>{{ languages( {"zh-cn": "zh-cn/JavaScript/Guide/Inheritance_and_the_prototype_chain" } ) }}</p>
Revert to this revision