[JavaScript] Prototype

2020. 8. 2. 23:21Programming Languages/JavaScript

처음 접하는 개념이라 하루종일 이것만 보고 있었네요 ㅠㅠ 조금은 이해한 것 같습니다.

 

1. Prototype Object

 

자바스크립트는 프로토타입 기반의 객체지향 언어입니다. Java와 C++에 있었던 Class 개념과 비슷하지만 차이가 꽤 있습니다. 자바스크립트를 잘 이해하기 위해서는 핵심 개념인 Prototype을 잘 이해해야 합니다.

 

클래스 기반의 객체지향 언어에서는 클래스를 선언하고 내부에 객체(인스턴스)를 생성합니다. 하지만 프로토타입 기반의 객체지향 언어에서는 클래스 필요없이 객체를 생성할 수 있습니다.

 

   자바스크립트에서 모든 객체는 자신의 부모 역할을 하는 객체와 연결되어 있다. 이것이 Java 등에 있는 상속의 개념이다. 자식 객체는 부모의 프로퍼티와 메소드를 사용할 수 있다. 이러한 부모 객체를 Prototype Object 또는 Prototype이라고 한다.

 

Prototype Object는 생성자 함수에 의해 생성된 각각의 객체에 공유 프로퍼티를 제공하기 위해 사용됩니다. 말은 어렵지만 쉽게 말하면 생성자를 통해 만든 객체들에게 같은 프로퍼티와 메소드를 사용하도록 만드는 것입니다.

 

자바스크립트의 모든 객체는 [[Prototype]]이라는 내부 슬롯(internal slot)을 가집니다. 이 값은 null 또는 객체이며 상속을 구현하는데 사용됩니다. [[Prototype]]의 값은 Prototype Object이며 __proto__ 로 접근할 수 있습니다. __proto__ 프로퍼티에 접근하면 내부적으로 Object.getPrototypeOf가 호출되어 Prototype Object를 반환합니다. student Object는 __proto__ 프로퍼티로 자신의 부모 Prototype Object인 Object.prototype을 가리키고 있습니다.

 

 

2. [[Prototype]] vs prototype 프로퍼티

 

모든 객체는 자신의 Prototype Object를 가리키는 [[Prototype]]을 가집니다. 함수도 객체이므로 이를 가집니다. 그런데 함수 객체는 일반 객체와 달리 prototype 프로퍼티도 가집니다. 여기서 주의할 점은 prototype 프로퍼티와 [[Prototype]]은 다르다는 점입니다.

 

  • [[Prototype]]
    • 함수를 포함한 모든 객체가 가지고 있는 인터널 슬롯입니다.
    • 객체의 입장에서 자신의 부모 역할을 하는 Prototype Object를 가리키며 함수 객체의 경우 Function.prototype을 가리킵니다. 
  • prototype 프로퍼티
    • 함수 객체만 가지고 있는 프로퍼티입니다.
    • 함수 객체가 생성자로 사용될 때 이 함수를 통해 생성될 객체의 부모 역할을 하는 객체, 즉 Prototype Object를 가리킵니다.

3. constructor 프로퍼티

Prototype Object는 constructor 프로퍼티를 가집니다. 이 프로퍼티는 객체의 입장에서 자신을 생성한 객체를 가리킵니다. 아래 코드를 보겠습니다.

 

 

1. joy의 [[Prototype]]은 Person의 prototype object을 가리킵니다.

2. joy 객체를 만든 객체는 Person의 생성자 함수입니다.

3. Person() 생성자 함수를 만든 객체는 Function() 생성자 함수입니다.

 

4. Prototype chain

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

 

 

분명 person 객체는 hasOwnProperty라는 메소드를 가지고 있지 않으므로 에러가 발생해야 한다고 생각할겁니다. 이는 person 객체의 [[Prototype]]이 가리키는 링크를 따라올라가서 person 객체의 부모인 Object의 prototype object의 메소드에 있는 hasOwnProperty를 호출한 것입니다. Java나 C++을 배우셨다면 이해하는데 조금은 수월한 부분입니다.

 

4.1 객체 리터럴 방식으로 생성된 객체의 프로토타입 체인

객체 생성 방법에는 3가지가 있습니다.

  • 객체 리터럴
  • 생성자 함수
  • Object() 생성자 함수

객체 리터럴 방식으로 생성된 객체는 결국 내장 함수인 Object() 생성자 함수로 객체를 생성하는 것을 단순화시킨 겁니다. 자바스크립트 엔진은 객체 리터럴로 객체를 생성하는 코드를 만나면 내부적으로 Object() 생성자 함수를 사용하여 객체를 생성합니다.

 

 

Object() 생성자 함수는 물론 함수입니다. 따라서 함수 객체인 Object() 생성자 함수는 prototype 프로퍼티를 가지고 있습니다.

  • prototype 프로퍼티는 함수 객체가 생성자로 사용될 때 이 함수를 통해 생성된 객체의 부모 역할을 하는 객체, 즉 prototype object를 가리킵니다.
  • [[Prototype]]은 객체의 입장에서 자신의 부모 역할을 하는 객체, 즉 prototype object를 가리킵니다.

 

 

위 객체가 생성될 때 prototype chain의 동작 과정입니다.

 

객체를 선언했으므로 Object() 생성자도 실행됩니다.

Object의 prototype의 constructor는 Object 생성자를 실행시킵니다.

Object() 생성자는 함수이므로 prototype 프로퍼티가 생성됩니다. 그리고 부모 객체인 Function의 prototype object를 찾습니다.

Funtion.prototype에서 prototype object를 찾았습니다.

이제 Object에는 [[prototype]]와 prototype 프로퍼티가 있습니다.

 

4.2 생성자 함수로 생성된 객체의 프로토타입 체인

 

생성자 함수로 객체를 생성하기 위해서는 우선 생성자 함수를 정의해야 합니다.

함수를 정의하는 방식에는 3가지 방법이 있습니다.

  • 함수 선언식
  • 함수 표현식
  • Function() 생성자 함수

함수 선언식으로 함수를 정의한다면 함수 리터럴 방식을 사용합니다.

함수 선언식으로 함수를 정의한다면 자바스크립트 엔진이 내부적으로 기명 함수표현식으로 변환됩니다.

 

결국 함수 선언식, 함수 표현식 모두 함수 리터럴 방식을 사용합니다. 함수 리터럴 방식은 Function() 생성자 함수로 함수를 생성하는 것을 단순화시킨것입니다.

즉, 3가지 함수 정의 방식은 결국 Function() 생성자 함수를 통해 함수 객체를 생성합니다. 따라서 어떤 방식으로 함수 객체를 생성해도 모든 함수 객체의 prototype 객체는 Function.prototype입니다. 생성자 함수도 함수 객체이므로 생성자 함수의 prototype object는 Function.prototype입니다.

 

정리를 해봅시다.

객체를 생성하는 방법에는 3가지가 있습니다. 3가지 객체 생성 방식에 의해 생성된 객체의 prototype 객체를 정리해보면 아래와 같습니다.

객체 생성 방식 엔진의 객체 생성 인스턴스의 prototype object
객체 리터럴 Object() 생성자 함수 Obejct.prototype
Object() 생성자 함수 Object() 생성자 함수 Object.prototype
생성자 함수 생성자 함수 생성자 함수 이름.prototype

 

 

1. kim 객체를 Person 생성자를 통해 생성합니다. kim 객체는 Person의 prototype object를 찾습니다.

2. 함수도 객체이므로 Object의 prototype object의 [[Prototype]]을 찾습니다.

3. Person 생성자는 함수이므로 prototype을 생성합니다. Person의 prototype에는 이제 Object의 [[Prototype]]과 prototype 프로퍼티가 존재합니다. 

4. 그리고 Person 생성자는 부모 객체인 Function의 prototype object를 찾습니다.

5. 결과적으로 Person은 prototype(Object의 [[Prototype]], prototype 프로퍼티), Function의 [[Prototype]]을 얻습니다.

 

5. 프로토타입 객체의 확장

프로토타입 객체도 객체이므로 일반 객체와 같이 프로퍼티를 추가/삭제할 수 있습니다. 그리고 이렇게 추가/삭제된 프로퍼티는 즉시 프로토타입 체인에 반영됩니다. prototype object에 프로퍼티나 메소드를 추가하면 즉시 사용 가능하다는 말입니다.

 

6. 원시 타입의 확장

기본 원시 타입은 객체가 아닙니다. 그런데 String(), Number, Array() 등의 함수를 사용하면 객체와 유사하게 동작시킬 수 있습니다. 원시 타입에 해당하는(문자열은 String, 숫자는 Number 등) 함수에 prototype에 추가하는 방식입니다.

 

 

 

7. 프로토타입 개체의 변경

객체를 생성할 때 프로토타입이 결정됩니다. 결정된 prototype object는 다른 임의의 객체로 변경할 수 있습니다. 이건 부모 객체인 프로토타입을 동적으로 변경할 수 있다는 말입니다. 이러한 특징을 활용하여 객체의 상속을 구현할 수 있습니다. 주의할 점은 프로토타입 객체 변경 시점 이전에 생성된 객체는 변경 이전의 프로토타입 객체를 [[Prototype]]에 바인딩하고 객체 변경 시점 이후에 생성된 객체는 변경 이후의 프로토타입 객체를 [[Prototype]] 바인딩합니다.

 

 

8. 프로토타입 체인 동작 조건

 

객체의 프로퍼티를 참조하는 경우, 해당 객체에 프로퍼티가 없는 경우, 프로토타입 체인이 동작합니다.

객체의 프로퍼티에 값을 할당하는 경우, 프로토타입 체인이 동작하지 않습니다. 이는 객체에 해당 프로퍼티가 있는 경우, 값을 재할당하고 해당 프로퍼티가 없는 경우는 해당 객체의 프로퍼티를 동적으로 추가하기 떄문입니다.

'Programming Languages > JavaScript' 카테고리의 다른 글

[JavaScript] 객체 복사  (0) 2020.08.12
[JavaScript] 객체  (0) 2020.08.11
[JavaScript] 함수(Function)  (0) 2020.08.02
JavaScript 기초 - 형변환  (0) 2020.07.31
JavaScript 기초 - 자료형  (0) 2020.07.30