[JavaScript] 객체 지향 프로그래밍 (2) - 프로토타입

선정·2022년 5월 26일
0

Today I Learned

  • 프로토타입
    • 프로토타입과 클래스
    • 프로토타입 체인

프로토 타입

프로토타입과 클래스

JavaScript는 프로토타입 기반 언어이며, 여기서 프로토타입은 원형 객체를 의미한다. 자바스크립트는 객체를 상속하기 위하여 프로토타입이라는 방식을 사용한다. 이는 자바스크립트의 모든 객체들이 메소드와 속성들을 상속 받기 위한 템플릿으로써 프로토타입 객체(prototype object)를 가진다는 것을 의미한다.

프로토타입 객체도 또 다시 상위 프로토타입 객체로부터 메소드와 속성을 상속 받을 수도 있고 그 상위 프로토타입 객체도 마찬가지이다. 이를 프로토타입 체인(prototype chain)이라 부르며 다른 객체에 정의된 메소드와 속성을 한 객체에서 사용할 수 있도록 하는 근간이다.

정확히 말하자면 상속되는 속성과 메소드들은 각 객체가 아니라 객체의 생성자의 prototype이라는 속성에 정의돼있다.

자바스크립트에서는 객체 인스턴스와 프로토타입 간에 연결(많은 브라우저들이 생성자의 prototype 속성에서 파생된 __proto__ 속성으로 객체 인스턴스에 구현하고 있다.)이 구성되며 이 연결을 따라 프로토타입 체인을 타고 올라가며 속성과 메소드를 탐색한다.

Object.prototype.__proto__ (deprecated)
Object.prototype의 __proto__ 속성은 접근하고자 하는 객체의 내부 속성인 [[Prototype]](객체 또는 null)를 노출하는 접근자 속성(getter 및 setter 함수)이다.


class Human {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  
  sleep() {
    console.log(`${this.name}은 잠에 들었습니다`);
  }
}

let steve = new Human('steve', 30);

Human.prototype.constructor === Human; // true
Human.prototype.constructor === steve.__proto__.constructor // true
Human === steve.__proto__.constructor; // true

Human.prototype === steve.__proto__; // true
Human.prototype.sleep === steve.sleep; // true


Human이라는 클래스와 인스턴스, 그리고 프로토타입의 관계


Array 클래스와 인스턴스, 그리고 프로토타입의 관계


배열을 예로 들자면, 하나의 배열은 여러 메서드를 갖는다. (e.g. Array.prototype.concat(), Array.prototype.push() 등)
Array 클래스의 prototype에 이 많은 메서드들이 정의돼있고, 우리가 배열 리터럴이나 new 키워드로 만든 배열 인스턴스들은 상속을 통해 배열 메서드에 접근할 수 있다.

모든 배열이 concat, push 등의 별개의 메서드를 갖는다기보다 하나의 프로토타입이 있고 각각의 배열이 __proto__ 혹은 [[Prototype]] 라는 특별한 특성으로 그 프로토타입을 참조하는 것이다.

const array1 = []; // 배열 리터럴로 인스턴스 생성
const array2 = new Array(); // new 키워드로 인스턴스 생성


프로토타입 체인

모든 객체는 프로토타입이라는 다른 객체를 가리키는 내부 링크를 가지고 있다. 즉, 프로토타입을 통해 직접 객체를 연결할 수 있는데 이를 프로토타입 체인이라 한다. 자바스크립트에서 모든 인스턴스는 프로토타입 체인을 통해 프로토타입 객체의 메소드를 참조할 수 있다.

특정 객체의 속성, 메소드에 접근할 때 해당 객체에 해당 속성이나 메소드가 존재하지 않는다면, 부모 클래스의 프로토타입 객체의 속성이나 메소드를 차례로 검색해서 실행하려 한다.


Person 클래스 생성 (ES6 - 클래스 문법)

class Person {
  constructor(first, last, age, gender, interests) {
    this.name = {
      first,
      last
    };    
    this.age = age;
    this.gender = gender;
    this.interests = interests;
  }
  
  greeting() {
    console.log(`Hi! I'm ${this.name.first}`);
  };
  
  farewell() {
    console.log(`${this.name.first} has left the building. Bye for now!`)
  };
}

let mina = new Person("mina", "kim", 20, "female", ["coding", "movie"]);
mina.interests; // ["coding", "movie"]
mina.greeting(); // Hi! I'm mina

Person 클래스의 하위 클래스인 Teacher 클래스 생성

  • Teacher 클래스는 Person 클래스와 공통된 속성과 메소드를 포함하면서 Person 클래스에는 없는, Teacher 클래스만의 속성(subject, grade)을 추가적으로 가진다.
  • extends 키워드를 통해 상속 받을 클래스를 명시한다.
class Teacher extends Person {
  constructor(first, last, age, gender, interests, subject, grade) {
    this.name = {
      first,
      last
    };
    this.age = age;
    this.gender = gender;
    this.interests = interests;
    this.subject = subject; // specific to Teacher
    this.grade = grade; // specific to Teacher
  }
}      

상위 클래스인 Person 클래스와 중복되는 속성 및 메소드는 super() 의 매개변수를 통해 상속 받을 수 있다.

super
super 키워드는 부모 객체(상위 클래스)의 함수(생성자)를 호출할 때 사용된다.

  • 문법
super([arguments]); // 부모 생성자 호출
super.functionOnParent([arguments]);

Teacher 클래스에 super 키워드 사용

class Teacher extends Person {
  constructor(first, last, age, gender, interests, subject, grade) {
    super(first, last, age, gender, interests);
    
    this.subject = subject; 
    this.grade = grade;
  }
}

// 변수 suzi는 Teacher 클래스의 인스턴스
let suzi = new Teacher('suzi', 'lee', 30, 'female', ['flower', 'drawing'], 'math', 3);

suzi.subject; // 'math' => subject 속성은 Teacher 클래스의 속성
suzi.greeting(); // 'Hi! I'm suzi' => greeting 메소드는 Person 클래스의 메소드

Teacher 클래스의 인스턴스를 생성하면 TeacherPerson 클래스의 속성과 메소드를 다 사용할 수 있다.


super 키워드 더 알아보기

상위 클래스 Human

class Human {
  constructor(name="no_name", age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
    this.animal = true;
  }
  greeting() {
    return `Hello, I'm ${this.name}`;
  }
}

하위 클래스 Student에서의 super 키워드

// (1) 또는 (2) 방법으로 Human 클래스의 인스턴스를 생성할 수 있음

 // (1)
class Student extends Human {
  constructor(name, age, gender, grade) {
    super(name, age, gender);
    this.grade = grade;
    this.adult = false;
  }
  greeting() {
    return super.greeting();
  }
}

// (2)
class Student extends Human {
  constructor(name, age, gender, grade) {
    super()
    this.name = name;
    this.age = age;
    this.gender = gender;
    this.grade = grade;
    this.adult = false;
  }
  greeting() {
    return super.greeting();
  }
}

DOM과 프로토타입

let div = document.createElement('div');

div.__proto__
// === HTMLDivElement.prototype
div.__proto__.__proto__
// === HTMLElement.prototype
div.__proto__.__proto__.__proto__
// === Element.prototype
div.__proto__.__proto__.__proto__.__proto__
// === Node.prototype
div.__proto__.__proto__.__proto__.__proto__.__proto__
// === EventTarget.prototype
div.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__
// === Object.prototype
div.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__
// null

document.createElement() 메소드로 만든 div 엘리먼트 역시 부모 클래스의 프로토타입, 혹은 부모의 부모 클래스의 프로토타입을 검색할 수 있다. 즉, DOM도 상속 관계로 이루어져있다는 의미이다. __proto__ 속성을 통해 상속관계를 확인할 수 있다.


참고

profile
starter

0개의 댓글