[컴][웹][자바스크립트] __proto__ 와 prototype

__proto__와 prototype 의 차이 / 비교 / proto 란? / prototype 이란?





요새 한창 javascript 작업을 해서, prototype 을 이용한 OOP programming 을 많이 하고 있다. 그런데 기본적으로 inheritance(상속)이 되지 않아서 편법(?) 을 사용하고 있는데 여기서 prototype 궁금 해져서 살펴보게 됐다.

이 부분을 몰라도 programming 하는 데에는 전혀 지장이 없다. 그냥 개인적인 호기심과 code 에 대한 이해도 향상을 위한 삽질일 뿐이다. 물론 이런 이해가 좋은 coding 을 하는데에 도움이 될 수는 있다고 생각하지만, 오히려 생각이 많아져 코딩이 느려지는 단점이 될 수도 있다.^^



먼저 설명을 위해서 용어에 대한 정의를 설명하도록 하자.

object 란?

먼저 여기서 이야기하는 object 의 개념을 조금 명확히 하자. javascript 에서 object 라 함은 일반적인 primitive type 이외의 모든 것들이 object type 이라 이녀석들을 object 라고 한다.[ref. 2] java 등에서 이야기하는 class 의 instance 를 object 라고 부르는 것과 조금 다르게 primitive type 이외의 모든 type 을 object 라고 부른다고 이해하자.


__proto__

__proto__ 는 예전에 사용하던 이름이다. 이 녀석을 ECMA 에서는 Prototype 이라고 부른다. 그리고 접근(access) 하는 방법도
a.__proto__
가 아니라 아래같은 함수를 이용하도록 하고 있다.
Object.getPrototypeOf(a)
여하튼 이 글에서는 혼동을 피하기 위해 __proto__ 라는 이름을 사용하도록 하겠다.


prototype

javascript 에서 prototype 이라는 이름의 member(property) 를 가지는 녀석은 함수(function)뿐이다.[ref. 2] 이 prototype 을 위의 __proto__ 와 구분지어서 "prototype property" 라고 부른다. 그러나 여기서는 그냥 __proto__ 와 prototype 으로 구분지어 이야기할 것이다.



Java class VS Javascript function

이제 __proto__ 와 prototype 의 차이를 알아보자. 이 __proto__ 와 prototype 의 차이를 이해하기 위해서 여기서는 우리가 기존에 알고 있는 Java 같은 OOP 언어의 관점에서 설명을 하도록 하겠다.


function A(){ ... } // 이 때 a 는 함수이기에 prototype 을 갖게 된다.

A.prototype
-> A {}
A.__proto__
-> function Empty() {}
var aa = new A()
-> undefined
aa.__proto__
-> A {}
aa.__proto__ == A.prototype
-> true
A.prototype.m1 = function() {...}
aa.m1()


이것을 java 의 class 와 비교해 보자. class A 가 new 되면(instantiate) 그 reference 인 aa 는 class 의 member variable 들을 property 로 가지게 된다.

class A{
 public int b;
        public A(){
          this.b = 3;
        }
 public void m1(){
  System.out.println("m1");
 }
}

A aa = new A();
System.out.println(aa.b);


prototype

그런데 javascript 에는 java 같은 oop 에서 사용하는 class 가 없다. 물론 javascript 의 object 를 이용해서 단순히 member 변수나 method 들을 가질 수 있다. 하지만 상속(inheritance) 등의 개념을 사용할 수는 없다. 그래서 prototype 이 필요하다. 이 prototype 에 member 들을 전부 넣어놓고는 inheritance 처럼 다른 object 에 member 들을 전달할 일이 있을 때 이 prototype 만을 넘겨주면 되도록 하였다. 이런식으로 oop 의 inheritance 가 가능하도록 해주는 것이다.(물론 다른 이점들도 있겠지만...^^)

그래서 function 이라는 object 에는 prototype 이라는 member 변수(property) 가 무조건 생기게 하였다. 약간의 추측이 들어간 글이지만, ref. 4 의 prototype 의 정의를 봐도 알 수 있듯이 이 prototype 의 목적은 member(property 라고도 부르는) 를 공유하기 위한 목적이다. 그리고 ref. 5 를 참고하면 좀 더 명확해 질 듯 하다. ref.2 에서 보면 이런 것을 prototypical inheritance mechanism 이라 부르는 듯 하다.


prototype 의 장점

이런 prototype 의 장점은 ref.2 에서도 확인할 수 있듯이 확장성이 좋다. 우리는 이 prototype 에 원하는 member 를 쉽게 추가할 수 있다. 굳이 상속을 안해도 기존에 존재하는 object 에 원하는 기능을 추가할 수 있게 된다.

A.prototype.mynewmethod = function(){ ... }
var a = new A()
a.mynewmethod()

이런 식으로 A 에 새로운 method 를 손쉽게 추가해서 사용할 수 있다.

Java 같은 class 에서는 class 를 직접 수정하지 않는이상 그 class 에 새로운 함수를 넣을 수 없지만, javascript 에서는 가능하다. prototype 을 직접 접근할 수 있기 때문이기도 하고, prototype 안의 method 를 찾아서 호출해 주기 때문이기도 하다.

이런 것을 mixin 이라 한다.



constructor

원래 oop language 에서는 new 를 하면 constructor 가 호출이 된다. 이런 기능을 javascript 에서 해주기 위해 new 를 할 때 constructor 라는 이름의 member(property) 를 추가해 주게 된다.

var a = new A()
javascript 에서 new 를 하게 되면 아래와 같은 일을 하게 된다.[ref. 3]
  1. 새로운 object a 를 만들고
  2. 만들어진 object a 의 constructor 변수(property) 를 만들고 이 constructor 변수에 function A 를 set 한다.
    a.constructor = A; // javascript code
  3. a.__proto__ 을 만들고 a.__proto__ 에 A.prototype(constructor 의 prototype) 을 할당한다.
    a.__proto__ = A.prototype; // javascript code
  4. 그리고는 constructor 를 호출한다.
위와 같은 작업을 통해 javascript 에서 constructor 가 가능하게 해준다. 위에서 이야기 했지만 constructor 도 하나의 function 이다. 그러므로 당연히 prototype 이라는 이름의 member 변수를 갖게 된다.



__proto__ , prototype

이제 위에서 설명한 이 둘의 차이를 정리해 보자.

__proto__ 는
  1. var a = new A() 처럼, new 로 instantiate 할 때 생겨나는 변수다.
  2. 여기에 constructor 의 prototype 인 A.prototype 을 할당하게 된다.

prototype 은
  1. function A(){...} 처럼 함수를 정의할 때 만들어 지는 변수다.
  2. 이 곳에 oop 의 member 에 해당하는 녀석들을 assign 하면 된다.
  3. 상속처럼 한 class 의 member 들을 그대로 가져오려 할 때 이 prototype 을 복사 해 오면 된다.




함수 정의 function declaration

우리가 아래처럼 함수를 정의(declaration) 하게 되면 Declaration Binding 할 때 함수가 instantiation 된다.

function Test() {}

javascript 가 parsed 되면 아래와 같은 과정을 거쳐서 정의해 놓은 함수를 처리하게 된다.[ref. 1]
  1. Source code is parsed
  2. -> JavaScript program is evaluated
  3. -> The global execution context is initialized 
  4. -> declaration binding instantiation is performed
10.5 Declaration Binding Instantiation





References

  1. var functionName = function() {} vs function functionName() {}
  2. Understanding JavaScript Prototypes.
  3. JavaScript constructors, prototypes, and the `new` keyword
  4. Annotated ECMAScript 5.1 > 4.3 Definitions
  5. ECMA-262-3 in detail. Chapter 7.2. OOP: ECMAScript implementation



댓글 없음:

댓글 쓰기