자바스크립트에서 객체의 작동 원리를 알아보자! 또한 작동 원리를 알기 위해 우리는 잠시 ES6의 Class 문법 이전으로 돌아갈 것이다.

 

1. 객체의 생성

 

let obj = {}

let obj = new Object()

//빈 객체를 선언과 동시에 할당하는 방법
//두 방법은 같은 결과를 초래한다.

  

  ES6 이전의 자바스크립트 세계에선 Class라는 개념이 없었다. 그렇다면 객체를 생성하기 위한 new Object()에서 Object는 무엇일까?

  여기서 Object는 바로 함수이다. 즉 ES6 이전에는 OOP를 함수로 구현했음을 짐작할 수 있다.

 

  함수는 클래스와 비슷하다. 함수 내부에서 여러 변수들과 함수를 정의할 수 있으며 여러 번 호출할 수 있기 때문이다.

  다음의 예시를 살펴보자.

 

function Animal(name, speed) {
  this.name = name;
  this.speed = speed;
  
  this.run = function () {
      console.log(`${this.name}가 ${this.speed}km/h의 속력으로 달립니다!`);
    }
  }
  
  const myDog = new Animal('doggy', '10');

 

  위와 같은 코드를 통해 Animal 타입의 myDog 라는 객체를 생성할 수 있다.

  그런데 위와 같은 코드는 하나의 문제를 가지고 있다.

  클래스 기반 객체 지향 프로그래밍에서 클래스에 메서드를 정의하면, 해당 클래스 타입의 모든 객체들은 하나의 메서드를 공유하게 된다. 

 

  하지만, 위와 같은 자바스크립트 코드는 다르다.

  이 코드는 Car 타입 객체가 생성될 때 마다 Drive라는 변수를 생성하고, 이 변수에 익명 함수를 저장하는 형태로 동작한다.

  즉, 객체가 생성될 때 마다 새로운 익명 함수가 생성되어 Drive에 저장되고 메모리 낭비가 심할 것이다.

  무엇보다 진정한 의미에서의 객체 지향을 구현한 것이 아니게 된다.

 

  자바스크립트에서 동일한 타입의 모든 객체가 하나의 메서드를 공유하도록 코드를 구현하려면, 위의 Animal 함수 정의에서 run 메서드 부분을 아래와 같이 고쳐야 한다.

 

function Animal(name, speed) {
    this.name = name;
    this.speed = speed;
}

Animal.prototype.run = function () {		<= 변경된 부분
    ...
}

const myDog = new Car("doggy", 10);

 

  앗 갑자기 생소한 것이 등장했다. prototype? 이 의미하는 것은 무엇일까

 

 

 

2. prototype

 

  prototype의 사전적 의미로는 원형이라는 뜻을 가지고 있다.

  위의 Animal 함수의 run 메서드를 모든 Animal 타입 객체들이 공유하게 만들기 위해 Animal.prototype이라는 곳에 run 메서드를 정의했다.

 

  Animal prototype이라는 변수는 모든 Animal 타입 객체들의 기준이 되는 단 하나의 '원형' 객체를 가리킨다.

  우리가 아래 코드처럼 Animal 함수를 정의하면, Animal 함수에는 run이라는 메서드가 존재하지 않는다.

 

 

 

  대신 Animal.prototype을 확인해보면 run 메서드가 존재함을 확인할 수 있다.

 

 

  자바스크립트는 myDog.run()을 통해 메서드를 호출할 때, 해당 객체에 Drive 메서드가 존재하지 않는다면 그 객체의 prototype으로 이동하여 run 메서드가 존재하는지 확인하고, 존재한다면 원형 객체의 run 메서드를 호출한다. 이런 방식으로 동작하기 때문에 Animal 타입의      모든 객체들이 동일한 원형 객체의 run 메서드를 호출하게 되는 것이다.

 

  그런데 원형 객체라는 것은 언제 만들어진 것이고, Animal.prototype이라는 건 어떻게 원형 객체를 가리키게 된 것일까?

  우리는 Animal에 prototype이라는 이름의 변수를 선언한 적도 없으며 Animal의 원형 객체라는 것도 만든 적이 없는데 말이다.

 

  자바스크립트에서는 우리가 어떤 함수를 정의하든 간에 해당 함수에 대한 Prototype 객체를 자동으로 생성해주며, 그 함수에 대해 '함수. prototype'이라는 변수도 생성한 후 이 변수가 Prototype 객체를 가리키도록 연결해준다. 지금 당장 콘솔에 가서 확인해보라.

 

  그냥 일반적인 함수 호출을 사용하기 위해 함수를 정의했다고 하더라도 자바스크립트는 무조건 해당 함수에 대한 Prototype 객체를 생성한다.

 

 

 

3. Constructor(생성자)

 

  constructor 함수는 클래스 기반 객체 지향 프로그래밍에서도 등장하는 익숙한 개념인 바로 그 생성자로,

  우리가 객체 생성을 요청할 때 호출되어 실제 객체를 만드는 역할을 하는 함수이다.

  즉 new Animal()라는 코드를 호출해 Animal.prototype.constructor 함수가 자동으로 호출되어 객체를 생성해준다.

 

  사실 자바스크립트에서는 Prototype 객체의 constructor가 기존 함수 자체를 가리키기 때문에, Animal.prototype.constructor는 function Animal(name, speed)를 가리킨다.

  

  단지, new 키워드가 함수 내부에서 사용한 this를 실제로 생성된 instance를 가리키도록 만들어주므로, 자바스크립트의 객체 생성에 있어 진짜 중요한 역할은 생성자가 아니라 new 키워드가 처리해준다고 볼 수 있다.

 

 

 

4. __proto__

 

  constructor 메서드는 오직 '함수의 Prototype 객체'만이 가진다.

  하지만 __proto__는 자바스크립트의 '모든 객체'에 대해 자동으로 추가되는 속성이다.

  

  그렇다면 이 __proto__에는 어떤 값이 담겨있을까?

 

  위 글에서 Animal.prototype이 가리키는 Prototype 객체에 Drive 함수를 정의하면, Animal 타입의 모든 객체들이 Prototype 객체의 run 함수를 사용하게 된다고 했었다.

  하지만 자바스크립트가 myDog.run()라는 코드를 보고 Prototype 객체의 run 메서드를 호출하려면, myDog 객체에서 Prototype 객체에 참조할 수 있어야 한다.

이를 위해 자바스크립트는 모든 객체의 인자로 Prototype 객체를 가리키는 __proto__라는 변수를 정의해준다.

  

  즉 위 예제에서 myDog.__proto__ === Animal.prototype 인 것이다.

 

  따라서, myDog.run()라는 코드가 실행되면 자바스크립트는 다음과 같은 과정을 거쳐 Prototype 객체의 run 메서드를 호출한다.

  1. myDog 객체에 run 메서드가 있는지 확인한다.

  2. myDog 객체에 run 메서드가 없으면, Prototype 객체에 해당 메서드가 존재하는지 확인하기 위해 __proto__ 변수가 가리키는 객체로 이동한다.

  3. Prototype 객체에 run 메서드가 존재한다면, Prototype 객체의 run 메서드를 호출한다.

 

 

5. Prototype chain

 

  만약 Animal.prototype에 run 메서드를 정의하지 않아서, Prototype 객체에도 메서드가 존재하지 않는다고 가정해보자.

  그렇다면, 자바스크립트는 Animal 함수의 Prototype 객체에서 탐색을 종료할까?

 

  정답은 아니다. 자바스크립트에서 모든 객체는 최상위 객체로 Object라는 객체를 가진다.

  Prototype 객체도 결국 객체이므로 최상위 객체로 Object 객체를 가진다.

  따라서 Prototype 객체인 Animal.prototype의 __proto__ 속성은 아래와 같이 최상위 객체인 Object의 Prototype 객체를 가리키게 된다.

 

 

 

  이처럼 Prototype 객체들이 __proto__ 속성을 통해 연결되는 구조를 'Prototype Chain'이라는 용어로 부른다.

  자바스크립트는 어떤 함수의 Prototype 객체에서 원하는 메서드를 찾지 못한다면 해당 Prototype 객체의 __proto__ 속성이 가리키는 부모 Prototype 객체로 이동한다. 즉 찾을 때까지 계속 거슬로 올라가는 것이다.

 

  이 작업은 Object 함수의 Prototype 객체에 도달할 때까지 반복되며, Object.prototype 객체는 최상위 객체이므로 __proto__ 속성의 값이 null이기 때문에 더 이상 이동할 수 있는 부모 Prototype 객체가 존재하지 않아 작업을 중단한다.

 

 

 

후.. 이해하는데 너무 어려운 주제였다. 블로깅 내용의 80%는 다음의 블로그를 참조했다!

 

 

 

출처 : velog.io/@piecemaker/3.-%EC% 9E%90% EB% B0%94% EC% 8A% A4% ED%81% AC% EB% A6% BD% ED% 8A% B8% EC%97%90% EC%84%9C-%EA% B0% 9D% EC% B2% B4% EA% B0%80-%EB% 8F%99% EC% 9E%91% ED%95%98% EB% 8A%94-%EB% B0% A9% EC% 8B%9D-%EC% 9D% B4% ED%95% B4% ED%95%98% EA% B8% B0

 

 

 

'CodeStates' 카테고리의 다른 글

IM 7일차 (Stack, Queue)  (0) 2021.01.19
IM 4일차 (Pesudoclassical)  (0) 2021.01.15
IM 4일차 (OOP와 4가지 특징)  (0) 2021.01.14
IM 3일차 (ESLint와 rules)  (0) 2021.01.13
IM 2일차 (ES6 문법)  (0) 2021.01.12

+ Recent posts