본문 바로가기
🦎모던 자바스크립트 Deep Dive

[Chap 19] 프로토타입

by egg.silver 2024. 5. 22.


19.1 객체지향 프로그래밍

- 객체 지향 프로그래밍 : 객체의 집합으로 프로그램을 표현하려는 프로그래밍 패러다임

- 객체 : 속성을 통해 여러 개의 값을 하나의 단위로 구성한 복합적인 자료구조

// 이름과 주소 속성을 갖는 객체
const person = {
  name: 'Lee',
  address: 'Seoul',
};
console.log(person); // {name: "Lee", address: "Seoul"}

 

아래는 원이라는 개념을 객체로 표현해 본 것이다.

const circle = {
  radius: 5, // 반지름
  //원의 지름: 2r
  getDiameter() {
    return 2 * this.radius;
  },
  // 원의 둘레: 2πr
  getPerimeter() {
    return 2 * Math.PI * this.radius;
  },
  // 원의 넓이: πrr
  getArea() {
    return Math.PI * this.radius ** 2;
  },
};
console.log(circle);
// {radius: 5, getDiameter: f, getPerimeter: f, getArea: f}
console.log(circle.getDiameter()); // 10
console.log(circle.getPerimeter()); // 31.41592653589793
console.log(circle.getArea()); // 78.53981633974483

 

 객체지향 프로그래밍은 객체의 상태를 나타내는 데이터와 상태 데이터를 조작할 수 있는 동작을 하나의 논리적인 단위로 묶어 생각한다.
 따라서 객체상태 데이터와 동작을 하나의 논리적인 단위로 묶은 복합적인 자료구조라고 할 수 있다.
 이때 객체의 상태 데이터를 프로퍼티(property), 동작을 메서드(method)라 부른다.


19.2 상속과 프로토타입

- 상속(inheritance) : 어떤 객체의 프로퍼티 또는 메서드를 다른 객체가 상속받아 그대로 사용할 수 있는 것을 의미

- 자바스크립트프로토타입 기반으로 상속 구현하여 불필요한 중복 제거

- 생성자 함수의 문제

   : 동일한 생성자 함수에 의해 생성된 모든 인스턴스가 동일한 메서드를 중복 소유하는 것은 메모리를 불필요하게 낭비함
   : 또한 인스턴스를 생성할 때마다 메서드를 생성하므로 퍼포먼스에도 악영향을 줌
   : 만약 10개의 인스턴스를 생성하면 내용이 동일한 메서드도 10개 생성되는 것임

 

아래 예시는 프로토타입을 기반으로 상속 구현한 예시

//생성자 함수
function Circle(radius) {
  this.radius = radius;
}
// Circle 생성자 함수가 생성한 모든 인스턴스가 getArea 메서드를
// 공유해서 사용할 수 있도록 프로토타입에 추가한다
// 프로토타입은 Circle 생성자 함수의 prototype 프로퍼티에 바인딩되어 있다
Circle.prototype.getArea = function () {
  return Math.PI * this.radius ** 2;
};
// 인스턴스 생성
const circle1 = new Circle(1);
const circle2 = new Circle(2);
// Circle 생성자 함수가 생성한 모든 인스턴스는 부모 객체의 역할을 하는
// 프로토타입 Circle.prototype으로부터 getArea 메서드를 상속받는다
//즉, Circle 생성자 함수가 생성하는 모든 인스턴스는 하나의 getArea 메서드를 공유한다
console.log(circle1.getArea === circle2.getArea); // true
console.log(circle1.getArea()); // 3.141592653589793
console.log(circle2.getArea()); // 12.566370614359172

└ Circle 생성자 함수가 생성한 모든 인스턴스는 자신의 프로토타입,
     즉 상위(부모)객체 역할을 하는 Circle,prototype의 모든 프로퍼티와 메서드를 상속받음

메서드 중복 생성 ➔ 상속에 의한 메서드 공유


19.3 프로토타입 객체

- 프로토타입 객체

   : 객체 간 상속을 구현하기 위해 사용

   : 어떤 객체의 상위(부모)객체의 역할을 하는 객체로서 다른 객체에 공유 프로퍼티(메서드 포함)를 제공

- 모든 객체는 [[Prototype]]이라는 내부 슬롯을 가지며, 이 내부 슬롯의 값은 프로토타입의 참조임

- [[Prototype]]에 저장되는 프로토타입은 객체 생성 방식에 의해 결정됨

     객체가 생성될 때 객체 생성 방식에 따라 프로토타입이 결정되고 [[Prototype]]에 저장

    └ 객체 리터럴에 의해 생성된 객체의 프로토타입은 Object.prototype

    └ 생성자 함수에 의해 생성된 객체의 프로토타입은 생성자 함수의 prototype 프로퍼티에 바인딩되어 있는 객체임

- 모든 객체는 하나의 프로토타입을 가지며, 모든 프로토타입은 생성자 함수와 연결되어 있음

객체와 프로토타입과 생성자 함수는 서로 연결되어 있다

[[Prototype]] 내부 슬롯에는 직접 접근할수 없지만, 위 그림처럼 __proto__ 접근자 프로퍼티를 통해 자신의 프로토타입,
즉 자신의 [[Prototype]] 내부 슬롯이 가리키는 프로토타입에 간접적으로 접근 가능

 

 

19.3.1  _ _proto_ _ 접근자 프로퍼티

- 모든 객체는 _ _proto_ _ 접근자 프로퍼티를 통해 자신의 프로토타입, 즉 [[Prototype]] 내부 슬롯에 간접적으로 접근 가능

 

( 1 ) _ _proto_ _는 접근자 프로퍼티

- 내부 슬롯은 프로퍼티가 아니기 때문에 자바스크립트는 원칙적으로 내부 슬롯과 내부 메서드에 직접적으로 접근하거나 호출할 수 있는 방법을 제공하지 않음

- 단, 일부 내부 슬롯과 내부 메서드에 한하여 간접적으로 접근할 수 있는 수단을 제공
- [[Prototype]] 내부 슬롯에도 직접 접근할 수 없으며 __proto_ 접근자 프로퍼티를 통해 간접적으로 [[Prototype]] 내부 슬롯의 값, 즉 프로토타입에 접근

 

( 2 ) __proto__ 접근자 프로퍼티는 상속을 통해 사용됨

- __proto__ 접근자 프로퍼티는 객체가 직접 소유하는 프로퍼티가 아니라 Object.prototype의 프로퍼티임
- 모든 객체는 상속을 통해 Object.prototype.__proto__ 접근자 프로퍼티를 사용 가능

 

( 3 ) __proto__ 접근자 프로퍼티를 통해 프로토타입에 접근하는 이유

- 상호 참조에 의해 프로토타입 체인이 생성되는 것을 방지하기 위함

- 프로토타입 체인은 단방향 링크드 리스트로 구현되어야 함

const parent = {};
const child = {};
// child의 프로토타입을 parent로 설정
child.__proto__ = parent;
// parent의 프로토타입을 child로 설정
parent.__proto__ = child; // TypeError: Cyclic __proto__ value

서로가 자신의 프로토타입이 되는 비정상적인 프로토타입 체인

 

( 4 ) __proto__ 접근자 프로퍼티를 코드 내에서 직접 사용하는 것은 권장하지 않음

- 모든 객체가 __proto__ 접근자 프로퍼티를 사용할 수 있는 것은 아니기 때문

- __proto__ 접근자 프로퍼티 대신 프로토타입의 참조를 취득하고 싶은 경우에는 Object.getPrototypeOf 메서드를 사용하고,
프로토타입을 교체하고 싶은 경우에는 Object.setPrototypeOf 메서드를 사용을 권장

const obj = {};
const parent = { x: 1 };
// obj 객체의 프로토타입을 취득
Object.getPrototypeOf(obj); // obj.__proto__;
// obj 객체의 프로토타입을 교체
Object.setPrototypeOf(obj, parent); // obj.__proto__ = parent;
console.log(obj.x); // 1

 

 

19.3.2  함수 객체의 prototype 프로퍼티

- 함수 객체만이 소유하는 prototype 프로퍼티는 생성자 함수가 생성할 인스턴스의 프로토타입을 가리킴

// 함수 객체는 prototype 프로퍼티를 소유
(function () {}).hasOwnProperty('prototype'); // — true
// 일반 객체는 prototype 프로퍼티를 소유하지 않음
({}).hasOwnProperty('prototype'); // — false

 

- 모든 객체가 가지고 있는(엄밀히 말하면 Object.prototype으로부터 상속받은) __proto__ 접근자 프로퍼티와 함수 객체만이 가지고 있는 prototype 프로퍼티는 결국 동일한 프로토타입을 가리킴

// 생성자 함수
function Person(name) {
this.name = name;
}
const me = new Person('Lee');
// 결국 Person.prototype과 me.__proto__는 결국 동일한 프루투타입을 가리킴
console.log(Person.prototype === me.__proto__); // true

객체의 __proto__ 접근자 프로퍼티와 함수 객체의 prototype 프로퍼티는 결국 동일한 프로토타입을 가리킴

 

 

19.3.3  프로토타입의 constructor 프로퍼티와 생성자 함수

-모든 프로토타입은 constructor 프로퍼티를 가지며, 이 constructor 프로퍼티는 prototype 프로퍼티로 자신을 참조하고 있는 생성자 함수를 가리킴
- 이 연결은 생성자 함수가 생성될 때, 즉 함수 객체가 생성될 때 이뤄짐

//생성자 함수
function Person(name) {
  this.name = name;
}
const me = new Person('Lee');
// me 객체의 생성자 함수는 Person
console.log(me.constructor === Person); // true

➔  me 객체에는 constructor 프로퍼티가 없지만 me 객체의 프로토타입인 Person.prototype에는 constructor 프로퍼티가 있음
     따라서 me 객체는 프로토타입인 Person.prototype의 constructor 프로퍼티를 상속받아 사용할 수 있음


19.4 리터럴 표기법에 의해 생성된 객체의 생성자 함수와 프로토타입

- 리터럴 표기법에 의해 생성된 객체도 프로토타입이 존재하지만, 리터럴 표기법에 의해 생성된 객체의 경우 프로토타입의 constructor 프로퍼티가 가리키는 생성자 함수가 반드시 객체를 생성한 생성자 함수라고 단정할 수는 없음

// obj 객체는 Object 생성자 함수로 생성한 객체가 아니라 객체 리터럴로 생성
const obj = {};
// 하지만 obj 객체의 생성자 함수는 Object 생성자 함수
console.log(obj.constructor === Object); // true

 

- Object 생성자 함수 호출과 객체 리터럴의 평가는 추상 연산 OrdinaryObjectCreate를 호출하여 빈객체를 생성하는 점에서 동일하나 new.target의 확인이나 프로퍼티를 추가하는 처리 등 세부 내용은 다름
➔  따라서 객체 리터럴에 의해 생성된 객체는 Object 생성자 함수가 생성한 객체가 아님

- 리터럴 표기법에 의해 생성된 객체도 상속을 위해 프로토타입이 필요
- 따라서 리터럴 표기법에 의해 생성된 객체도 가상적인 생성자 함수를 가짐
- 프로토타입은 생성자 함수와 더불어 생성되며 prototype, constructor 프로퍼티에 의해 연결되어 있기 때문

- 프로토타입과 생성자 함수는 단독으로 존재할 수 없고 언제나 쌍으로 존재

리터럴 표기법에 의해 생성된 객체의 생성자 함수와 프로토타입

'🦎모던 자바스크립트 Deep Dive' 카테고리의 다른 글

[Chap 21]빌트인 객체  (0) 2024.06.03
[Chap 20]strict mode  (0) 2024.05.29
[Chap 17] 생성자 함수에 의한 객체 생성  (0) 2024.05.15
[Chap 15] 스코프  (0) 2024.05.07
[Chap 13] 스코프  (0) 2024.05.01