[TIL] Prototype. 상속과 위임 그 애매한 경계 너머

June hyoung Park·2020년 8월 24일
0

JavaScript

목록 보기
3/18
post-thumbnail

자바스크립트의 모든 객체는 최소한 하나 이상의 다른 객체로부터 상속을 받으며, 이때 상속되는 정보를 제공하는 객체를 프로토타입(prototype)이라고 합니다. -Tcp School

Prototype?💁🏻

자바 스크립트는 Java나 C++처럼 클래스 기반의 언어가 아닌 프로토타입 기반의 언어인 만큼 프로토타입은 자바 스크립트를 공부하는 사람이라면 누구나 짚고 넘어가야하는 개념중 하나인 만큼 굉장히 중요한 개념이다.

자바스크립트의 모든 객체는 부모객체가 가진 프로퍼티나 메서드를 상속받아 호출하는것이 가능한데, 이러한 부모객체를 프로토타입(prototype) 객체라 한다.

[[prototype]]..?

const Person = function(name){
    this.name = name;
    this.hello = function(){
        console.log(`Hello ${this.name}`);
    }};

const james = new Person('james'); 

Person이라는 생성자 함수를 'new'연산자를 통해 호출했더니 Person에서 정의한 내용을 바탕으로 새로운 instance가 생성되었다. 그리고 이 instance에는 __proto__라는 Property가 자동으로 부여되는데 __proto__은 자바 스크립트의 모든 객체가 자신의 부모인 프로토타입 객체를 가리키는 참조링크를 저장하는 객체이다.

❗️(ES5.1 명세에는 __proto__가 아니라 [[prototype]]이라는 명칭으로 기재되어있으며, __proto__은 브라우저가 [[prototype]]을 구현한 대상에 지나지않는다.)

모든 객체는 함수를 통해 생성된다? 🤷🏻‍♂️

먼저 프로토타입의 원리를 알기 위해서는 객체는 언제나 함수를 통해 생성된다는 사실을 기억해야한다.
객체 리터럴 방식으로 생성한 객체든, 생성자 함수로 생성한 객체든 결국 함수로 생성한 객체이다.

const newObj = new Object(); // 자바 스크립트 기본 API인 Object함수로 생성

const newObj = {}; // 객체 리터럴 방식으로 생성, (위와 같은 코드이다.)

함수를 통해 객체가 생성 된다면 객체가 생성된 후엔 무슨일이 일어나는지 알아보자.


객체 리터럴 방식을 통한 객체 생성

객체 리터럴 방식으로 생성한 객체도 결국 Object()라는 자바스크립트 내장 api 함수를 통해 생성된 객체이다.
또한 모든 생성자 함수는 생성시 prototype객체가 함께 생성되는데, 이로인해 생성된 Object.prototype 객체에는 api 함수들이 내장되어있다.

이로인해 'myObject'객체는 __proto__ 링크를 통해 hasOwnProperty()같은 Object.prototype의 메서드를 사용할 수 있는것이다. 이처럼 부모의 프로토 타입 객체의 프로퍼티를 자신것 처럼 사용할수있고, 이를 프로토타입 체이닝이라고 한다.

(myObject 객체는 hasOwnProperty()메서드를 갖고있지않지만, 부모의 프로토타입 객체가 ``hasOwnProperty()```메서드를 갖고있기때문에 사용할 수 있다.)

생성자 함수를 통한 객체 생성 방식

1. 생성자 함수 Person 생성시 Person.prototype 객체가 생성됨.
2. Person.prototype의 constructor는(생성자)는 생성자 함수 Person이다.
3. new 연산자를 통해 james라는 객체를 생성했더니 이 객체는 __proto__ 라는 암묵적인 링크를
통해 Person의 prototype객체에 접근이 가능해짐 (객체 리터럴방식에서 myObject가 __proto__링크를 통해 부모의 프로토타입 객체에 접근하는것과 같은 원리이다.)
4. Person.prototype 역시 자바스크립트 객체 이기에 Object.prototype을 prototype객체로 가진다. 따라서 프로토 타입 체이닝은 Object.prototype으로 이어진다.

Person 생성자 함수로 생성된 james와 생성자 함수인 Person의 프로퍼티를 보면 각각 james의 __proto__ 와 Person 함수의prototype 이 같은 곳을 가리키고있는것을 볼수있다.

모든 객체의 종착역 Object

앞에서 살펴봤듯이 객체 리터럴 생성 방식이든 생성자 함수를 통한 객체 생성 방식이든 결국 모든 객체의 프로트 타입 체이닝의 끝은 Object.prototype이다. 그렇기에 모든 객체는 toString, isPrototypeOf, hasOwnProperty()같은 내장 메서드를 사용할 수 있는것이다.

그래서 어쩌라고? 🤷🏻‍♂️

이를 통해 우린 객체지향 언어의 class를 비슷하게 흉내낼 수 있다. 위 예제에서 객체 리터럴로 생성한 myObject는 Park라는 name 프로퍼티를 갖고있고, Person 생성자 함수로 생성한 james객체는 james라는 name 프로퍼티를 갖고있다. 만일 프로토타입을 몰랐다면 두 객체에 console.log(`hello ${this.name}!`)를 실행하는 sayHello라는 함수를 만들어주고 싶다면

const Person = function(name){
    this.name = name;
    this.sayHello = function(){
        console.log(`hello ${this.name}!`);
    }};

const james = new Person('james'); 
james.sayHello(); // hello james!

------------------------------------------------

const myObject = {
  name: "Park",
  sayHello: function(){
    console.log(`hello ${this.name}!`)
  }
}

myObject2.sayHello(); // // hello Park!

위와 같이 각 객체의 프로퍼티 함수로 만들어 줬겠지만, 이제 프로토타입에 대해 알게됬으므로 아래와 같이 모든 객체의 부모 프로토타입 객체인 Object.prototype에 sayHello 함수를 만들어줌으로서 모든 객체에서 sayHello객체에 접근할 수 있게 되었고, 이로인해 우린 불필요한 메모리 낭비를 줄일 수 있게되었다!

마지막으로 중요한 것은 자바스크립트의 프로토타입 작동방식과 일반적인 객체 지향언어 에서 쓰는 상속의 개념과는 큰 차이가 있는데, 자바스크립트에서 객체간의 관계는 복사가 아닌 연결이 맺어진 것임으로 상속보단 ‘위임’이 좀더 적절한 표현이라고 한다.

여담

(제게 있어선) 상당히 복잡한 개념과 관련된 포스팅이라 잘못된 부분이 있을 수 있습니다! 언제든 잘못된 부분은 댓글로 남겨주시면 바로 수정하도록 하겠습니다! 😃


(약 4시간에 걸쳐 블로그를 쓰던 내 모습.)

profile
Take me home~~~~

0개의 댓글