2oneweek.dev

20. Prototype이란?

    Tags

  • JavaScript
20. Prototype이란? thumbnail

Prototype

prototype은 자바스크립트의 객체지향성을 지탱하는 개념이다. 또 일반적인 객체지향 언어와 구분하는 개념이다. prototype은 말 그대로 객체의 원형이 된다. JS에서는 prototype으로 상속을 구현할 수 있다. 근데 JS ES6부터 class 키워드가 등장하지만, 이 prototype의 개념을 벗어나지 않는다.

JS에서 Prototype이란 명칭은 2가지를 각각 가리킨다. 이 두 가지를 구분할 수 있어야 한다.

  1. Prototype property
  2. Protytpe Link



1) JS에서 함수

function Person(){} 함수는 statement의 일종이지만, JS에서 함수는 독특한 "객체"다. 객체이기 때문에 new 키워드를 사용해서 함수를 표현할 수도 있다. var Person = new Function()


function Person(name, first, second) { this.name = name; this.first = first; this.second = second; }
  • 위와 같이 함수를 선언하면 Person이라는 function Object가 생성된다.
    • 내부에 prototype 이라는 프로퍼티가 생긴다.
  • 그런데 객체가 하나 더 생긴다. Person.prototype이라는 객체도 추가로 생성된다. 이것은 prototype Object 라고 불린다.
    • 내부에 constructor라는 프로퍼티가 생긴다.


2) Function Object 와 Function.prototype Object

  • Penson (함수)객체와 Person.prototype 객체는 서로 상호참조하고 있다.
    • Person의 prototype "property"가 Person.prototype 객체를 참조한다.
    • Person.prototype의 constructor "property"가 Person 객체를 참조한다.

함수는 객체다. 따라서 프로퍼티를 가질 수 있다.

function Object는 prototype이라는 프로퍼티를 가지는데, 그 프로퍼티는 객체의 원형인 prototype object를 참조하고 있다.

  • function object에는 프로퍼티 이름이 prototype인 프로퍼티가 있으며, prototype이라는 프로퍼티가 참조하는 것은 prototype obejct다. 이름이 겹쳐서 헷갈릴 수 있음



3) new + 함수 = 생성자

생성자는 기본적으로 함수다. 그런데 함수를 호출할 때 new 키워들르 사용하면, 그 함수는 단순한 함수가 아니라 생성자로서 호출된다.


생성자는 객체를 반환한다.

function Person() {} var obj = new Person(); console.log(typeof Person); //=> function console.log(typeof obj); //=> object
  • Person의 타입은 function이지만, obj의 타입은 object다.

new로 선언할 경우 this는 전역 객체가 아닌 생성된 객체 자체를 가리키게 된다.

var name = "default name"; function Person(name) { if (this.name) { console.log(this.name); } else { this.name = name; console.log(this.name); } } Person("cho"); //=> default name var obj = new Person("cho"); //=> cho
  • Person()을 그냥 호출하면, this는 글로벌 오브젝트를 참조한다.
    • 따라서, default name을 출력하게 된다
  • new 키워드로 호출하면, this는 함수 그 자체에 바인딩된다.
    • 내부 동작은 정확히 모르겠으나, 새로 생성되는 객체를 Scope로 둔 후, 프로퍼티를 설정하고 객체가 반환되는 것 같다.

생성자는 객체의 원형에 부합하는 프로퍼티(변수나 메소드)를 가진 객체를 반환한다.

function Person(name, age) { this.name = name; this.age = age; } var kim = new Person("kim", 29);
  • 객체 kimnameage를 가진 객체가 된다.
  • 생성자에 의해 객체의 프로퍼티가 define 된다.



4) .__proto__.prototype(프로퍼티)

함수는 생성자가 되어, 객체를 생성할 수 있다.

var kim = new Person("kim", 10, 20); var cho = new Person("cho", 20, 30);
  • 생성자 함수를 호출과 new 키워드가 합쳐지면서, kim과 cho에 Person (함수)객체와 같은 새로운 객체가 생성된다.
  • 이때 Person 객체가 가지고 있는 프로퍼티들을 만들고 초기화한다.
  • 또한 __proto__라는 프로퍼티가 생긴다. 이 프로퍼티는 Person.prototype 객체를 참조한다.

function Person(name, age) { this.name = name; this.age = age; } Person.prototype.name = "default name"; Person.prototype.age = "default age"; var kim = new Person("kim", 29); console.log(Person.prototype === kim.__proto__); //=> true
  • console.log(Person.prototype === kim.__proto__); 콘솔에서는 prototype property는 오브젝트 마다 다르다. 인스턴스에서는 .__proto__ 라는 프로퍼티가 프로토타입 객체를 참조하고, 클래스와 같은 함수 원형은 .prototype.이라는 프로퍼티로 프로토타입 객체를 참조한다.



  • new를 통해 생성되는 객체는 __proto__ 프로퍼티로, 생성자는 prototype 프로퍼티로 prototype Object(Person.prototype) 을 참조한다.
  • Prototype Object는 constructor라는 프로퍼티로 생성자 (function Person) 를 참조한다.

Prototype link란

prototype link란 prototype chain이라고도 불린다. 스코프 체인의 원리와 비슷하다.

function Person(name, age) { this.name = name; this.age = age; } Person.prototype.getName = function () { console.log(this); return this.name; }; var cho = new Person("이한주", 27); var kim = new Person("김한주", 18); console.log(cho); //=> {name : 이한주, age: 27} console.log(kim); //=> {name : 김한주, age :18} console.log(cho.getName()); //=> 이한주 console.log(kim.getName()); //=> 김한주
  • Person.prototype.getName = function(){...} Person (함수)객체가 아니라 Person.prototype 객체에 getName이라는 메소드를 추가했다.
    • 따라서, cho,kim 객체에는 getName이라는 메소드가 없다.

식별자 해결

  • 그러나 cho.getNamekim.getName은 정상적으로 동작한다.
  • chokim 객체에는 getName이라는 메소드가 없는데 어떻게 식별자 해결이 동작할까?
  1. 엔진은 처음 각자의 객체에서 식별자 해결을 한다.
  2. 만약 현재 위치에서 식별자 해결이 되지 않는다면, __proto__(prototype property) 가 참조하는 객체로 올라가서 식별자 해결을 하게 된다.
  3. __proto__가 참조하는 것은 현재 Person.prototype Object이므로, getName에 대한 식별자 해결이 가능하다.

이렇게, prototype이 참조하는 대상으로 올라가는 것은, 객체와 프로토타입이 연결되어 있기 때문인데, 이것을 prototype link라고 한다.

이렇게 Prototype Object에 프로퍼티를 추가하면서, prototype link로 연결된 객체들이 Prototype Object에 위치한 변수나 메소드를 사용할 수 있다. 즉, prototype object는 관련 객체들끼리의 공유 장소가 될 수 있다.


this 바인딩

  1. 자바스크립트 엔진은 cho.getName이 호출되면, getName을 위한 실행 컨텍스트를 만든다. 이때, getName 호출문 앞에 위치한 cho라는 객체를 this 바인딩 컴포넌트로 참조한다.
  2. getName의 실행시점에서, this로 참조하는 cho 오브젝트로 가서 name이라는 식별자에 대한 식별자 해결을 하게되고 값을 리턴하게 된다.



Prototype link를 이용한 상속

function First(v) { this.first = "first"; } function Second() { this.second = "second"; } function Third() { this.third = "third"; } First.prototype.get = function () { console.log(this.first, this.second, this.third); }; Second.prototype = new First(); Third.prototype = new Second(); var obj = new Third();
  • Second.prototype 의 참조 대상을 First형 객체로 바꿔치기 했다.
  • Third.prototype 의 참조 대상을 Second형 객체로 바꿔치기 했다.
  • obj가 실제적으로 들고 있는 프로퍼티는 third뿐이지만, 마치 부모 클래스가 있는 것 처럼, secondfirst 속성을 갖게 된다.(스코프 체인에 의하여) 이런 방식으로 상속을 구현한다.

Class 상속과 다른 점은, 상속에서 부모 클래스의 프로퍼티들을 "복사하지 않는 것이다." 프로토타입 체인만을 이용하여, 내 것처럼 사용할 수 있게 되는 것이다. 스코프 체인과 동작 원리가 같다.

식별자 해결에 있어서 프로토타입과 스코프체인이 같이 동작한다고 한다. 이 동작 원리에 대해서 공부가 더 필요하다.

Written by@2-one-week
현재 블로그 개발 중

GitHubLinkedIn