프로토타입

1TBhard·2020년 12월 14일
0

FrontEnd면접

목록 보기
4/7
post-thumbnail

📌 "ProtoType 기반 상속에 대해 설명해주세요."


Prototype?

JavaScript는 객체지향 언어이지만 다른 언어와 다르게 class 가 존재하지 않는다.
따라서, JavaScript는 class 에 있는 상속 기능이 존재하지 않는다.

대신 자신의 부모 역할을 담당하는 객체와 연결하여 상속 등을 흉내낼 수 있다.
이러한 부모 객체를 Prototype 객체 혹은 Prototype 이라고 한다.

이런 Prototype은 JavaScript의 모든 객체는 객체가 정의됬을 때 자동으로 생성되고 초기화된다.

📖 JS에서 객체를 생성하면 [[Prototype]]이라는 은닉된 프로퍼티을 가지고 자신의 Prototype이 되는 다른 객체를 가르킨다.

Prototype을 사용함으로서 아래의 장점을 가질 수 있다.

  • 객체와 객체를 연결해서 상속(멤버함수, 멤버변수를 공유)할 수 있다.
  • 객체가 Prototype프로퍼티를 상당수 상속할 수 있어서 각 객체에 필요한 메모리의 양을 줄일 수 있다.

    💡 프로퍼티(property) : 객체 내부의 속성()으로"키": "값"으로 구성된다.

⚠️ JavaScript는 객체지향 언어라면 당연히 있어야할 클래스가 없다.
ES6class 가 추가되어도 문법으로서 추가된 것이지 클래스 기반이 아닌 Prototype 기반 언어이다.


1. 예제

아래와 같이 프로퍼티를 공유할 수 있다.

var A = { a: "hi" };
var B = { b: "hello" };

// A의 __proto__ 는 B를 가리키게 된다. 즉, A의 부모가 B가 된다.
// ⚠️ 그러나 실제로 A에게 B.b 를 주는 것이 아니다.
// 이는 프로토타입 체인을 통해 해당 값을 찾아가는 것이다. (이는 아래의 나중에 설명하겠다.)
A.__proto__ = B;

console.log(A.b);        // "hello";
B.b = "bye bye";
console.log(A.b);        // "bye bye";

2. __proto__prototype

  • __proto__

    • 모든 객체가 가지고 있는 속성
    • 객체가 생성될 때 자신의 부모 객체의 prototype을 가리킨다.
  • prototype

    • 함수 객체만이 가지는 프로토타입
    • 함수 객체가 생성자로 사용될 때 인스턴스의 부모 역할을 하는 객체를 가리킨다.

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

var foo = new Person('Lee');

console.dir(Person); // prototype 프로퍼티가 있다.
console.dir(foo);    // prototype 프로퍼티가 없다.

console.log(foo.__proto__ === Person);           // false

// __proto__ 는 부모 객체의 프로토타입을 가리킨다.
console.log(foo.__proto__ === Person.prototype); // true

// 함수도 객체이기 때문에 함수의 프로토트입의 __proto__ 는 Object의 prototype 을 가리킴
console.log(Person.prototype.__proto__ === Object.prototype);        // true

⚠️ __proto__ 은 legacy 이다.
대신에 Object.getPrototypeOf(), Object.setPrototypeOf() 을 사용해야한다.

📖 __proto__ 의 역사

  • 생성자 함수의 prototype 프로퍼티는 아주 오래전부터 그 기능을 수행하고 있었다.
  • 그런데 2012년, 표준에 Object.create가 추가되면서 주어진 프로토타입을 사용해 객체를 만들 수 있긴 하지만, 프로토타입을 얻거나 설정하는것은 불가능했다.
    그래서 브라우저는 비표준 접근자인 __proto__를 구현해 언제나 프로토타입을 얻거나 설정할 수 있도록 하였다.
  • 이후 2015년에 Object.setPrototypeOf와 Object.getPrototypeOf가 표준에 추가되면서 __proto__와 동일한 기능을 수행할 수 있게 되었다.

Prototype의 식별자 룩업

C++, JAVA와 같은 언어는 객체를 만든 시점에서 상속구조를 통해 어떤 멤버들을 가지고 있는지 이미 알고 있다.

이와 다르게 JavaScript는 식별자를 찾는(룩업) 방법으로 두 종류의 메커니즘을 사용하여 원하는 식별자를 찾을 때까지 상위 객체를 참조한다.

  • 프로토타입 룩업 : 프로토타입 체인을 통해 원하는 속성이 나올때까지 계속 객체의 메서드나 속성을 찾아나간다.
  • 스코프 룩업 : 스코프 체인을 통해 원하는 속성이 나올때까지 계속 객체의 메서드나 속성을 찾아나간다.

💡 식별자 : 변수, 상수, 함수, 클래스 등의 이름
💡 프로토타입 체인 : 어떤 객체의 프로퍼티 를 찾아내는 메커니즘
💡 스코프 체인 : 어떤 객체(전역 객체가 아닌)의 프로퍼티 이 아닌 식별자를 찾아내는 메커니즘

var a = {
    attr1: 'a'
};

var b = {
    __proto__: a,
    attr2: 'b'
};

b.attr1;         // 'a'
  1. b객체 내부에 attr1 속성을 찾는다. -> 없다.
  2. b객체에 __proto__ 속성이 존재하는지 확인한다. -> 있다.
  3. b객체의 __proto__ 속성이 참조하는 객체로 이동한다. -> a객체로 이동
  4. a객체 내부에 attr1 속성을 찾는다. -> 있다.
  5. 찾은 속성의 값을 리턴한다.
    • 만약 찾는 값이 없으면 undeifned를 반환한다.

Prototype 으로 만들 수 있는 것

1. 생성자

Prototype으로 생성자를 Object.prototype.constructor 를 이용하여 만들 수 있다.

var obj = {};

console.log(obj.constructor === Object.prototype.constructor);    // true
console.log(obj.constructor === obj.__proto__.constructor);       // true

생성자를 이용하여 객체를 생성하면 객체는 생성자의 프로토타입 객체와 프로토타입 체인 으로 연결된다.

아래의 예제를 통해 이를 확인해보자

// 생성자
function Student(str) {
    this.name = str;
}

Student.prototype.getName = function() {
    return this.name;
};

var std = new Student('GilDong');

위 코드는 JS 엔진에서 아래와 같이 진행된다.

function Student(str) {
    this.name = str;
}

// 호이스팅
var std;

std = {};                           // 새 객체를 만듦
Student.call(std, "GilDong");       // call을 사용하여 Student 의 this를 설정 및 함수를 호출
std.__proto__ = Student.prototype;  // 프로토타입을 연결함

std.getName();                      // "GilDong" 출력

2. 상속

상속을 하는 법은 크게 2가지로 나뉘어진다.

  • Object.create() 사용(이것도 결국 Prototype 을 이용)

    var a = { attr1: 'a' };
    var b = Object.create(a);
    
    b.attr2 = 'b';
  • Prototype 을 이용

    var a = { attr1: 'a' };
    
    function Ghost() {}
    Ghost.prototype = a;
    
    var b = new Ghost();
    
    b.attr2 = 'b';

좀 더 어려운 예

function Parent(num) {
  this.num = num;
}

Parent.prototype.getNumber = function() {
  return this.num;
};

function Child(num, name) {
  // call을 사용해 Parent의 this를 바인딩 및 num으로 인자로 주어 호출
  Parent.call(this, num);
  this.name = name;
}

Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

Child.prototype.getName = function() {
  return this.name
}

var c = new Child(1, "Hong Gil Dong");
console.log(c.getNumber());                // 1
console.log(c.getName());                  // "Hong Gil Dong"

결론

  • Prototype이란 자신의 부모 역할을 담당하는 객체이다.

  • JavaScript는 Class가 존재하지 않지만 Prototype을 이용하여 Class처럼 비슷하게 구현할 수 있다.

    • ES6 구문에 Class가 존재하지만 JavaScript가 Class기반의 언어가 된 것은 아니다
  • 객체가 정의됬을 때 자동으로 Prototype이 생성되고 초기화된다.

  • JavaScript는 식별자를 찾는(룩업) 방법으로 프로토타입 체인 을 통해 원하는 속성이 나올때까지 계속 객체의 메서드나 속성을 찾아나간다.



참조

profile
기억을 넘어 습관으로

0개의 댓글