자바스크립트 프로토타입

hb-developer·2021년 6월 9일
0

JavaScript

목록 보기
9/10

본 내용은 책 모던 자바스크립트 Deep Dive 의 내용을 참조했습니다.

프로토타입

자바스크립트에서 객체는 부모 역할을 하는 객체의 프로퍼티나 메서드를 상속받는다
이렇게 상속해주는 부모 객체를 프로토타입객체 혹은 프로토타입 이라고 한다.

필요성

프로토타입이 필요한 이유를 알아보자.

// 생성자 함수
function Circle(radius) {
  this.radius = radius;
  this.getArea = function () {
    // Math.PI는 원주율을 나타내는 상수다.
    return Math.PI * this.radius ** 2;
  };
}

// 반지름이 1인 인스턴스 생성
const circle1 = new Circle(1);
// 반지름이 2인 인스턴스 생성
const circle2 = new Circle(2);

// Circle 생성자 함수는 인스턴스를 생성할 때마다 동일한 동작을 하는
// getArea 메서드를 중복 생성하고 모든 인스턴스가 중복 소유한다.
// getArea 메서드는 하나만 생성하여 모든 인스턴스가 공유해서 사용하는 것이 바람직하다.
console.log(circle1.getArea === circle2.getArea); // false

console.log(circle1.getArea()); // 3.141592653589793
console.log(circle2.getArea()); // 12.566370614359172

생성자 함수는 쉽게 객체를 찍어낼수 있다.

문제는 객체안에 메서드가 동일함에도 불구하고 각각의 객체를 생성한다.

중복된 코드를 따로 생성하면 메모리가 낭비되고 유지보수도 어려워진다.

이때 프로토타입을 기반으로 상속하면 불필요한 중복을 없앨수 있다.

// 생성자 함수
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

프로토타입 체인

프로토타입은 서로를 순환참조 하는걸 방지하기 위해
단방향 링크드 리스트로 구성되있다.

A 라는 객체에 프로퍼티를 접근할때 만약 존재하지 않으면
A 에서 먼저 검색한뒤 B --> C 순으로 올라간다.
그렇게 쭉 올라가다 보면 Object.prototype 이 나온다.

Object.prototype 은 모든 프로토타입의 종점역 이다.

프로토타입 객체

모든객체는 [[prototype]] 이라는 내부객체를 가지는데 이것은 null 혹은 객체를 참조한다.
여기서 참조대상이 프로토타입 이다.

그렇다면 [[prototype]] 에 접근하려면 어떻게 해야할까?
[[prototype]] 은 숨김프로퍼티 라서 직접 참조는 불가능하다.
따라서 __proto__ 를 사용해서 간접적으로 접근한다.

__proto__ 접근자 프로퍼티

const obj = {};
const parent = { x: 1 };

// getter 함수인 get __proto__가 호출되어 obj 객체의 프로토타입을 취득
obj.__proto__;
// setter함수인 set __proto__가 호출되어 obj 객체의 프로토타입을 교체
obj.__proto__ = parent;

console.log(obj.x); // 1

__proto__ 접근자는 프로토타입을 get 하거나 set 할수있다.

__proto 접근자는 Object.prototype 로부터 상속 받은 프로퍼티다.
즉 모든객체는 __proto
거의 모든객체에서 사용할수있다.

🧨 Object.create() 함수는 객체를 만드는 함수다.
인자 안에 계승할 프로토타입을 넣어줄수 있는데 일반적인 객체를 넣어주면 프로토타입의 종점인
Object.prototype 를 상속 받는다. 하지만 null 을 넣어주면 자기 자신이 프로토타입의 종점인
객체가 생성된다. 즉 __proto__ 접근자 포함 Object.prototype 의 모든 프로퍼티를 상속받지 않는 객체가 생성된다.

즉 __proto__ 는 [[Prototype]] 의 getter setter 함수라고 할수있다.

현재 모던한 자바스크립트에서는 어떨까?
__proto__ 접근자는 사실 ES5까지는 비표준 접근자였다.

ES6 부터 공식으로 호환성을 위해 표준화를 했지만 추천하지도 권장하지도 않는다.
따라서 프로토타입을 불러오거나 설정 하려면 다음과 같은 메서드를 사용한다

  • Object.getPrototypeOf(obj)obj의 [[Prototype]]을 반환합니다.
  • Object.setPrototypeOf(obj, proto)obj의 [[Prototype]]이 proto가 되도록 설정합니다.
  • Object.create(proto, [descriptors])[[Prototype]]이 proto를 참조하는 빈 객체를 만듭니다. 이때 프로퍼티 설명자를 추가로 넘길 수 있습니다.
profile
배우면 바뀐다

0개의 댓글