모자딥 19장 프로토타입

릿·2023년 2월 1일
0

19장 프로토타입

19.1 객체지향 프로그래밍


객체란?

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

19.2 상속과 프로토타입


  • 상속 : 객체지향 프로그래밍의 핵심 개념
  • 자바스크립트는 프로토타입을 기반으로 상속을 구현함
  • 생성자 함수로부터 생성된 인스턴스의 메서드 중복 생성 방지에 유용함
// 생성자 함수
function Circle(radius) {
  this.radius = radius;
}

// Circlr생성자 함수의 prototype프로퍼티에 바인딩하여
// 모든 인스턴스가 getArea메서드를 공유할 수 있도록 함
Circle.prototype.getArea = function () {
  return Math.PI * this.radius ** 2;
};

const circle1 = new Circle(1);
const circle2 = new Circle(2);

19.3 프로토타입 객체


  • 객체 간 상속을 구현하기 위해 사용됨
  • 모든 객체는 [[Prototype]]이라는 내부 슬롯을 가지고, [[Prototype]]에 저장되는 프로토타입은 객체 생성방식에 의해 결정됨
  • 모든 객체는 하나의 프로토타입을 갖고, 생성자 함수와 연결되어 있음

19.3.1 proto 접근자 프로퍼티

  • 모든 객체는 proto접근자 프로퍼티를 통해 [[Prototype]]내부 슬롯에 간접적으로 접근할 수 있다.
  • proto접근자 프로퍼티는 상속을 통해 사용됨 (Object.prototype의 프로퍼티임)
  • 사용하는 이유 : 상호 참조에 의해 프로토타입 체인이 생성되는 것을 방지하기 위함
  • proto접근자 프로퍼티를 코드 내에 직접 사용하는 건 권장하지 않음 (모든 객체가 사용할 수 있는 게 아니기 때문)
  • 프로토타입 참조를 취득하고 싶은 경우 : Object.getPrototypeOf메서드 사용 권장
  • 프로토타입을 교체하고 싶은 경우 : Object.setPrototypeOf메서드 사용 권장

19.3.2 함수 객체의 prototype프로퍼티

  • 생성자 함수가 생성할 인스턴스의 프로토타입을 가리킴
  • proto접근자 프로퍼티와 prototype프로퍼티는 동일한 프로토타입을 가리키나 사용하는 주체가 다르다.

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

  • 모든 프로토타입은 constructor프로퍼티를 가지고, 이는 prototype프로퍼티이며 자신을 참조하고 있는 생성자 함수를 가리킴

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

  • 리터럴 표기법에 의해 생성된 객체의 경우 가상적인 생성자 함수를 갖게 됨

19.5 프로토타입의 생성 시점

  • 프로토타입은 생성자 함수가 생성되는 시점에 생성됨

19.5.1 사용자 정의 생성자 함수와 프로토타입 생성 시점

  • construtor는 함수 객체를 생성하는 시점에 프로토타입시 생성됨
  • 생성된 프로토타입의 프로토타입은 언제나 Object.prototype임

19.5.2 빌트인 생성자 함수와 프로토타입 생성 시점

  • 빌트인 생성자 함수 : Object, String, Numer, Function, Array, RegExp, Date, Promise
  • 전역 객체가 생성되는 시점에 생성됨
  • 생성된 프로토타입은 빌트인 생성자 함수의 prototype프로퍼티에 바인딩 됨

19.6 객체 생성 방식과 프로토타입의 결정


  • 객체생성방법 : 객체 리터럴, Object생성자 함수, 생성자 함수, Object.create메서드, 클래스(ES6)
  • 자바스크립트 엔진은 객체를 생성할 때 추상 연산 OrdinaryObjectCreate를 호출함

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

  • 객체 리터럴에 의해 생성되는 객체의 프로토타입은 Object.prototype임

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

  • Object생성자 함수에 의해 생성되는 객체의 프로토타입은 Object.prototype임

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

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

19.7 프로토타입 체인


  • 정의: 해당 객체에 접근하려는 프로퍼티가 없다면 [[Prototype]]내부 슬롯의 참조를 따라 부모역할을 하는 프로토타입의 프로퍼티를 순차적으로 검색하는 것
  • 자바스크립트가 객체지향 프로그래밍의 상속을 구현하는 매커니즘
  • Object.prototype : 프로토타입 체인의 종점
  • 스코프체인과 프로토타입 체인은 서로 협력하여 식별자와 프로퍼티를 검색하는 데 사용됨

19.8 오버라이딩과 프로퍼티 섀도잉


  • 오버라이딩: 상위 클래스가 가지고 있는 메서드를 하위 클래스가 재정의하여 사용하는 방식
  • 오버로딩: 함수의 이름은 동일하지만 매개변수에 의해 메서드를 구별하여 호출하는 방식 (자바스크립트는 지원하지 않음)
  • 프로퍼티 섀도잉 : 상속관계에 의해 프로퍼티가 가려지는 현상

  • 하위객체를 통해 프로토타입의 프로퍼티 변경 / 삭제는 불가능함

19.9 프로토타입의 교체


19.9.1 생성자 함수에 의한 프로토타입의 교체

  • 생성자 함수의 prototype프로퍼티를 통해 프로토타입을 교체할 수 있는데, 이 때 constructor프로퍼티와 생성자 함수 간의 연결이 파괴되므로 constructor 프로퍼티를 추가하여 연결을 되살릴 수 있음
const Person = (function () {
  function Person(name) {
    this.name = name;
  }
  
  // 생성자 함수의 prototype프로퍼티를 통해 프로토타입 교체
  Person.prototype = {
  // constructor 프로퍼티와 생성자 함수 간의 연결을 설정
  	constructor: Person,
    sayHello() {
      console.log(`Hi! My name is ${this.name}`);
    }
  };
  
  return Person;
}());

19.9.2 인스턴스에 의한 프로토타입의 교체

  • 인스턴스의 proto접근자 프로퍼티(또는 Object.getPrototypeOf메서드)를 통해 프로토타입을 교체할 수 있는데, 이 때 constructor프로퍼티와 생성자 함수 간의 연결이 파괴되므로 constructor 프로퍼티를 추가하여 연결을 되살릴 수 있음
function Person(name) {
  this.name = name;
}

const me = new Person('Lee');

// 프로토타입으로 교체할 객체
const parent = {
  // constructor 프로퍼티와 생성자 함수 간의 연결을 설정
  constructor: Person,
  sayHello() {
    console.log(`Hi! My name is ${this.name}`);
  }
};

// me객체의 프로토타입을 parent객체로 교체한다.
// 아래 코드는 me.__proto__ = parent; 와도 같다.
Object.setPrototypeOf(me, parent);

19.10 instanceof 연산자


객체 instanceof 생성자 함수

  • 우변에 생성자 함수의 prototype에 바인딩된 객체가 좌변의 프로토타입 체인상 존재여부에 따라 bool값으로 반환
// 생성자 함수
function Person(name) {
  this.name = name;
}

const me = new Person('Lee');

// Person.prototype이 me 객체의 프로토타입 체인 상에 존재하므로 true로 평가됨
console.log(me instanceof Person); // true

19.11 직접 상속


19.11.1 Object.create에 의한 직접 상속

  • 명시적으로 프로토타입을 지정하여 새로운 객체를 생성하는 메서드
  • 첫번째 매개변수: 프로토타입으로 지정할 객체
  • 두번째 매개변수: 프로퍼티 키와 프로퍼티 디스크립터 객체로 이뤄진 객체 (옵션, 생략가능)
const obj = Object.create(Object.prototype, {
  x: { value: 1, writable: true, enumerable: true, configurable: true }
});

19.11.2 객체 리터럴 내부에서 proto에 의한 직접 상속 (ES6)

const myProto = { x: 10 };

const obj = {
  y: 20,
  __proto__: myProto
};

19.12 정적 프로퍼티/메서드

  • 생성자 함수로 인스턴스를 생성하지 않아도 참조/호출할 수 있는 프로퍼티/메서드 (프로토타입 체인상의 프로퍼티/메서드가 아닌 생성자 함수 자체가 가지고 있는 프로퍼티/메서드임)
// 생성자 함수
function Person(name) {
  this.name = name;
}

// 프로토타입 메서드
Person.prototype.sayHello = function() {
  console.log(`Hi! My name is ${this.name}`);
}
  
// 정적 프로퍼티
Person.staticProp = 'static prop';

// 정적 메서드
Person.staticMethod = function () {
  console.log('staticMethod');
}

19.13 프로퍼티 존재 확인


19.13.1 in 연산자

  • 객체 내에 특정 프로퍼티가 존재하는지 여부를 확인

    key in object

const person = {
  name: 'Lee',
  address: 'Seoul'
};

// person객체에 name프로퍼티가 존재한다
console.log('name' in person); // true
  • ES6에서 도입된 Reflect.has메서드를 사용해도 동일하게 작동함
console.log(Reflect.has(person, 'name')); // true

19.13.2 Object.prototype.hasOwnProperty메서드

  • 객체 고유의 프로퍼티 키인 경우에만 true를 반환하고, 상속받은 키인 경우엔 false를 반환함
console.log(person.hasOwnProperty('name'));

19.14 프로퍼티 열거


19.14.1 for...in문

  • 객체의 프로토타입 체인 상에 존재하는 모든 프로토타입의 프로퍼티 중에서 프로퍼티 어트리뷰트 [[Enumerable]]값이 true인 프로퍼티를 순회하며 열거함 (순서를 보장하지 않음)
  • 순회 대상 객체의 프로퍼티 뿐만 아니라 상속받은 프로퍼티까지 열거됨

    for (변수선언문 in 객체) { ... }

const person = {
  name: 'Lee',
  address: 'Seoul'
};

// for...in문의 변수 prop에 perso객체의 프로퍼티 키가 할당됨
for (const key in person) {
  console.log(key + ': ' + person[key]);
}
// name: Lee
// address: Seoul

19.14.2 Object.keys/values/entries메서드

  • 객체 자신의 고유 프로퍼티를 열거할 때 사용

1. Object.keys

  • 객체 자신의 열거 가능한 프로퍼티 키를 배열로 반환

2. Object.values (ES8)

  • 객체 자신의 열거 가능한 프로퍼티 값을 배열로 반환

3. Object.entries (ES8)

  • 객체 자신의 열거 가능한 프로퍼티 키와 값의 쌍의 배열을 배열에 담아 반환
const person = {
  name: 'Lee',
  address: 'Seoul',
  __proto__: { age: 20 }
};

console.log(Object.keys(person)); // ['name', 'address']
console.log(Object.values(person)); // ['Lee', 'Seoul']
console.log(Object.entries(person)); // [['name', 'lee'], ['address', 'Seoul']]
profile
항상 재밌는 뭔가를 찾고 있는 프론트엔드 개발자

0개의 댓글