프로토타입

프로토타입에 대해서 공부해보자

  • 프로토타입이란?

    • 엄연히 자바스크립트도 같은 객체지향 언어지만, 자바스크립트는 프로토타입 기반의 객체지향을 추구한다.

    • 자바스크립트는 클래스가 없이도 프로토타입을 기반으로하여 객체를 생성하는 빙식을 취한다.

    • 현 정리는 #13 과 함께 보길 권장한다.

    • 간단하게 정리하면 모든 자식 객체는 부모객체와 연결되있고, 최상위는 Object 객체이고 null 을 proto type 으로 참조한다.

  • 프로토타입의 구조

    • 모든 객체는 "[[Prototype]]"으로 명명된 인터널 슬롯(internal slot)를 가진다.

    • 인터널 슬롯의 경우에 원칙적으로는 접근을 할 수 없는 private 적인 특성을 취하고 있으나 경우에 따라 간접적인 접근이 가능하다.

    • 다시 본론으로 돌아가서, [[Prototype]]의 값은 null 또는 객체이며 상속을 구현하는데 사용된다. [[Prototype]] 객체의 데이터 프로퍼티는 get 액세스를 위해 상속되어 자식 객체의 프로퍼티처럼 사용할 수 있다. 하지만 set 액세스는 허용되지 않는다.

    • [[Prototype]]의 값은 Prototype(프로토타입) 객체이며 __proto__ accessor property로 접근할 수 있다. __proto__ 프로퍼티에 접근하면 내부적으로 Object.getPrototypeOf가 호출되어 프로토타입 객체를 반환한다.

    • 인터널 슬롯(internal slot)

      • 잠깐 인터널 슬롯이라는 것을 언급하고 넘어가자면 ECMAScript 스펙에서 요구하는 객체와 관련된 내부 상태와 내부 동작을 정의한 것이다.

      • 다시 말해, 내부 슬롯과 내부 메소드는 자바스크립트 엔진이 코드를 실행하는 알고리즘을 설명하기 위해 ECMAScript 스펙에서 사용하는 의사 프로퍼티(Pseudo property)와 의사 메소드(Pseudo method)이다.

      • ECMAScript 스펙에 등장하는 이중 대괄호([[…]])로 감싼 이름들이 내부 슬롯과 내부 메소드이다.

      • 따로 이슈를 할당해서 한번 정리한다.

  • [[Prototype]] 는 무엇이고 prototype property 는 무엇인가?

    • 객체는 프로토타입 인터널 슬롯을 취하고 있으나 함수는 프로토타입 프로퍼티도 함께 소유하고 있다.

    • 둘다 부모 객체를 가르키나 관점이 다르다고 소개가 되고 있다.

    • 함수 내부의 인터널 슬롯에서 보는 프로토타입

      • 객체의 입장에서 자신의 부모 역할을 하는 프로토타입 객체를 가리키며 함수 객체의 경우 Function.prototype 을 가리킨다.

      • 어떤 방식으로 함수를 생성하든 Function() 생성자 함수를 통해 함수 객체를 생성한다, 따라서 어떠한 방식으로 함수 객체를 생성하여도 모든 함수 객체의 prototype 객체는 Function.prototype 이다. 생성자 함수도 함수 객체이므로 생성자 함수의 prototype 객체는 Function.prototype 이다.

    • 함수 내부의 프로퍼티의 관점으로 보는 프로토타입

      • 함수 객체가 생성자로 사용될 때 이 함수를 통해 생성될 객체의 부모 역할을 하는 객체(프로토타입 객체)를 가리킨다.

  • 생성자 프로퍼티(constructor property)

    • 간단하게 이야기하면 자신을 생성한 생성자 함수를 프로퍼티로 가지고 있기 위해서 존재하는 프로퍼티다.

  • Prototype chain

    • 자바스크립트는 특정 객체의 프로퍼티나 메소드에 접근하려고 할 때 해당 객체에 접근하려는 프로퍼티 또는 메소드가 없다면 [[Prototype]]이 가리키는 링크를 따라 자신의 부모 역할을 하는 프로토타입 객체의 프로퍼티나 메소드를 차례대로 검색한다. 이것을 프로토타입 체인이라 한다.

  • 프로토타입 객체의 확장

    • 프로토타입도 엄연한 객체이기에 프로퍼티를 추가/삭제가 가능하고, 프로토타입 체인에 즉시 영향이 가게 된다.

    • 기초적인 프로토타입 객체를 확장시키는 코드를 살펴보자

        function Person(name) {
          this.name = name;
        }
      
        var foo = new Person('Lee');
      
        Person.prototype.sayHello = function(){
          console.log('Hi! my name is ' + this.name);
        };
      
        foo.sayHello();
  • 그림 설명

    • 생성자 함수 Person 은 프로토타입 객체 Person.prototype 와 prototype 프로퍼티에 의해 바인딩되어 있다.

    • Person.prototype 객체는 일반 객체와 같이 프로퍼티를 추가/삭제가 가능하다.

    • 위의 예에서는 Person.prototype 객체에 메소드 sayHello 를 추가하였다.

    • 따라서 생성자 함수 Person 에 의해 생성된 모든 객체는 프로토타입 체인에 의해 부모객체인 Person.prototype 의 메소드를 사용할 수 있게 되었다.

  • 원시 타입(Primitive data type)의 확장

    • 원시 타입 자체는 변경이 불가능하나 프로퍼티나 메소드를 호출할 때 원시 타입과 연관된 객체로 일시적으로 변환되어 프로토타입 객체를 공유하게 된다.

    • 마치 자바에서의 오토박싱? 과 같은 느낌이다.

  • 프로토타입 객체의 변경

    • 객체를 생성할 때 프로토타입은 결정된다.

    • 결정된 프로토타입 객체는 다른 임의의 객체로 변경할 수 있다.

    • 이것은 부모 객체인 프로토타입을 동적으로 변경할 수 있다는 것을 의미한다.

    • 이러한 특징을 활용하여 객체의 상속을 구현할 수 있다.

    • 기존 생성자 함수를 통해 생성된 객체의 prototype property 에 새로운 프로토타입의 객체를 할당하는 경우에 기존의 프로토타입과 관계가 깨지므로 잘 고려해서 코딩해야한다.

  • 프로토타입 체인 동작 조건

    • 특정 객체에 요청한 프로퍼티가 존재하지 않으면 프로토타입 체인에 의해서 탐색하지만 그 반대의 경우에는 동작하지 않는다.

Last updated