[Javascript] 프로토타입 복습

프로토타입 개념

- 자바스크립트는 프로토타입을 기반으로 상속을 구현 -> 코드 재사용에 용이

- 생성자 함수는 동일한 프로퍼티(메서드 포함) 구조를 갖는 객체를 여러 개 생성할 때 유용

- 생성자 함수에서 this로 할당된 프로퍼티,메서드는 인스턴스가 생성될때 할당된다.

// 아래 Circle 생성자 함수의 getArea는 모든 인스턴스가 가지는 메서드 이므로 메모리 낭비가 됨
function Circle(radius) {
  this.radius = radius;
  this.getArea = function () {
    return Math.PI * this.radius ** 2;
  }
}

// 프로토타입을 이용한 getArea 중복 제거
// 모든 인스턴스는 getArea 메서드를 공유함
function Circle(radius) {
  this.radius = radius;
}
Circle.prototype.getArea = function () {
  return Math.PI * this.radius ** 2;
}

 

메서드 중복 생성(왼), 메서드 공유(오)

 

- 모든 객체는 [[[Prototype]] 이라는 내부 슬롯을 가진다
  - 내부 슬롯의 값은 프로토타입의 참조값이다. -> 객체 생성 방식에 따라 결정됨

  - 내부 슬롯에는 직접 접근할 수 없다 (__proto__ 접근자 프로퍼티로 간접적으로 접근 가능)

  - __proto__ 는 프로토타입 상속을 통해 사용된다 (Object.prototype 의 프로퍼티이다.)

  - 프로토타입의 참조( [[Prototype]] 내부슬롯 ) 에 의해 프로토타입 체인이 이루어진다

  - 프로토타입 체인은 단방향 링크드 리스트이다. (내부슬롯을 직접 참조할 수 없는 이유)

 

prototype,  constructor, 객체 의 관계

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

객체 - 프로토타입 - 생성자함수 참조 방식

- 함수 객체의 prototype 프로퍼티는 생성자 함수가 생성할 인스턴스의 프로토타입을 가리킨다.

- non-constructor(화살표 함수, ES6 축약 표현 메서드) 는 프로토타입을 생성X, prototype 프로퍼티 X

- 객체의 __proto__ 와 함수의 prototype은 동일한 프로토타입을 가리킨다. 

- 모든 prototype은 constructor를 갖는다. (자신을 참조하고 있는 생성자 함수를 가리킴)

function Person(name) {
  this.name = name;
}

const me = new Person('KIM');

console.log(Person.prototype === me.__proto__); // true
console.log(me.constructor === Person); // true

 

 

객체 생성 방식에 따른 프로토타입의 결정

1. 객체 리터럴

const obj = { x: 1 };

console.log(obj.constructor === Object); // true
console.log(obj.hasOwnProperty('x')); // true

 

객체 리터럴에 의해 생성된 객체의 프로토타입

 

2. Object 생성자 함수

const obj = new Object();
obj.x = 1;

console.log(obj.constructor === Object); // true
console.log(obj.hasOwnProperty('x')); // true

Object 생성자 함수에 의해 생성된 객체의 프로토타입

3. 생성자 함수

function Person(name) {
  this.name = name;
}

Person.prototype.sayHello = function() {
  console.log(`Hi My name is ${this.name}`);
}

const me = new Person('Lee');
const you = new Person('Kim');

me.sayHello(); // Hi My name is Lee
you.sayHello(); // Hi My name is Kim

생성자 함수에 의해 생성된 객체의 프로토타입

4. Object.create 메서드

// 명시적으로 프로토타입을 지정함

// 1. Object.prototype을 상속받지 못함
let obj = Object.create(null); 
console.log(Object.getPrototypeOf(obj) === null); // true
console.log(obj.toString()); // TypeError

// 2. Object.prototype을 프로토타입으로 지정
// 객체 리터럴 생성 방식과 같음
obj = Object.create(Objet.prototype);
console.log(Object.getPrototypeOf(obj) === Object.prototype);

// 3. 임의의 객체를 직접 상속

// (1) 객체 리터럴
const myProto = { x:10 };
obj = Object.create(myProto);
console.log(obj.x); // 10

// (2) 생성자 함수
function Person(name) {
  this.name = name;
}
// obj = new Person('Lee'); 와 동일함
obj = Object.create(Person.prototype);
obj.name = 'Lee';

5. 클래스(ES6)

 

프로토타입 체인

- 객체의 프로퍼티에 접근할 때 해당 객체에 접근하려는 프로퍼티가 없다면 [[Prototype]] 내부 슬롯의 참조를 따라 부모 프로토타입의 프로퍼티를 순차적으로 검색

- 프로토타입 체인은 상속과 프로퍼티 검색을 위한 매커니즘이다.

- 스코프 체인과 프로토타입 체인은 서로 협력하여 식별자와 프로퍼티를 검색한다. (아래 me.hasOwnProperty('name'); 참고)

function Person(name) {
  this.name = name;
}

Person.prototype.sayHello = function() {
  console.log(`Hi My name is ${this.name}`);
}

const me = new Person('Lee');

// hasOwnProperty는 Object.prototype의 메서드
// me 객체가 Person.prototype 뿐만 아닌 Object.prototype도 상속 받았다는 것
// 스코프에서 me 식별자 검색 -> me 객체의 프로토타입 체인에서 프로퍼티(메서드) hasOwnProperty 검색
console.log(me.hasOwnProperty('name'); // true

// 프로토타입 체인
Object.getPrototypeOf(me) === Person.prototype // true
Object.getPrototypeOf(Person.prototype) === Object.prototype // true

프로토타입 체인