본문 바로가기

코드 기록

Restudy Series. Javascript (1)

728x90

Preview

자바스크립트라는 언어를 사용해 프로그램을 개발해왔지만 정작 내가 이 언어에 대해 잘 알고있는가에 대한 궁금증이 생겼다. 이 언어만이 가진 특성이나 다른 언어에 비해 가지고 있는 장점과 단점 등을 정확히 알고 사용하고 있는가에 대한 본질적인 궁금증이었다.
그래서 나만의 방식으로 Javascript 를 정의해보고 핵심 개념에 대해 정리해놓으려고 한다.
이번 포스팅에서는 프로토타입과 객체, 생성자의 개념에 대해 살펴보고자 한다.

Methodology

Javascript 의 특징

  1. 프로토타입 기반 언어이다.

프로토타입이라는 단어는 나를 포함한 많은 주니어 개발자들을 괴롭힌다(JS 면접 필수질문으로 뽑힌다). 도대체 프로토타입이란 무엇인가.

프로토타입이라는 낱말은 원초적 형태라는 뜻의 그리스어 낱말 πρωτότυπον (프로토타이폰)에서 왔다.
-위키 백과사전

프로토타입이란 어떤 유무형의 물체의 원초적인 형태라고 해석할 수 있다. 모든 사물에는 원형이 있다. 지나친 비유일 수 있겠지만 인간의 그 원형은 인간의 조상이라 불리우는 오스트랄로피테쿠스가 될 수 있고 또한 그 원시인 조차 조상이 존재하기에 그 원형은 태초의 생물체가 될 수 있다. 진화와 변형을 거칠 뿐 원형에 대해선 변함이 없다. 자바스크립트에서 프로토타입은 특정 객체를 생성한 생성자(원형) 객체를 가리킨다. 진화와 변형은 상속이란 개념으로 볼 수 있다. 원형의 객체로부터 상속받은 객체는 또 새로운 객체로 상속할 수 있다. 다만 생성된 객체는 몸속 어딘가에 부모의 정보(생성자)를 주소로서 담고있다. 주소로서 담고 있다는 말은 부모의 정보가 자식 객체에 그대로 복사된 형태가 아닌 그 정보를 찾을 수 있는 주소를 가리키고 있다는 말이다.
그렇다면 객체는 무엇일까.

  1. 객체(Object)

자바스크립트의 거의 모든 것은 객체이다. 기본 데이터 타입을 제외하고는 모두 객체이다. 객체는 데이터를 그룹화해서 그 그룹의 명칭(Key)에 따라 데이터(value)를 담은 정보 바구니인 셈이다. 바구니는 중괄호의 형태 ({}) 혹은 Object라 불린다. 이 바구니는 담겨있는 정보가 전혀 다른 바구니를 담을 수도 있고 셀수없이 많은 바구니도 담을 수 있는 도라에몽의 주머니와 같다.
객체를 만드는 방법은 간단하다.

var object = {}; //중괄호의 형태
var object = new Object(); //Javascript에서 새로운 객체를 만들 때는 new 키워드를 사용한다.
var person = {
  name: "bono",
  age: 25,
};
function Person(name) {
  this.name = name;
} //함수도 객체다.

7번 줄에서 생성한 Person 함수를 통해 여러 객체를 생성할 수 있다.

var tom = new Person("tom");
var jake = new Person("jake");
  1. constructor, prototype, __proto__

앞서 생성한 tom과 jake 는 Person 함수를 생성자로 해서 만든 새로운 객체이다.
constructor는 생성자를 가리키기 때문에 tom과 jake의 constructor 를 보면 Person임을 알 수 있다.

tom.constructor
> ƒ Person(name) {
  this.name = name;
}

Person은 Function으로 생성했기에 Person의 constructor는 함수(Function) 임을 알 수 있다.

Person.constructor
> ƒ Function() { [native code] }

Prototype을 설명하기 위해 새로운 예를 들어보려고 한다.

function Animal() {}

var dog = new Animal();
var cat = new Animal();

dog과 cat 은 Animal 이라는 생성자(함수)로 생성한 객체이다.

dog의 속성에 legsCount(다리개수)가 있는지 확인해보고 없으면 부여하려고 한다.

console.log(dog.legsCount);
> undefined;

dog에는 legsCount라는 속성을 가지고 있지 않기 때문에 undefined 가 출력된다. dog뿐만 아니라 cat의 legsCount 속성도 주고싶은 경우 어떻게 해야할까?

//(A)
Animal.legsCount = 4;
console.log(dog.legsCount);
> undefined

//(B)
Animal.prototype.legsCount = 4;
console.log(dog.legsCount);
> 4

(A)와 같은 방법으로 하면 dog와 cat에는 legsCount 속성이 상속되지 않는다. Animal 이라는 객체의 속성에 legsCount를 넣었을 뿐이지 원형을 변형시키진 않았다.
앞서 prototype은 원형을 가리킨다고 했다. dog 과 cat의 원형은 Animal이라는 객체의 원형(prototype)이다.

엄밀히 말하면 Animal의 prototype 에 legsCount를 넣은 것이지 dog.legsCount에 넣은 것은 아니다. 그런데 왜 dog.legsCount 에 Animal.prototype.legsCount가 출력된 것일까.
답은 프로토타입 체이닝 때문이다. Javascript 객체는 해당 객체의 속성이 존재하지 않으면 생성자의 prototype을 검사한다. dog의 legsCount는 없었기 때문에 dog의 생성자인 Animal의 prototype을 검사한 것이다. 이를 프로토타입 체이닝이라고 한다.
만약 Animal의 prototype에도 legsCount 가 없었다면 Animal의 생성자인 Function 의 prototype에서 legsCount를 찾았을 것이다.

만약 dog.legsCount 속성을 정의한다면 Animal의 prototype에 있는 legsCount가 변경될까?

dog.legsCount = 5;

답은 "아니오" 다. dog.legsCount라는 속성이 없는 경우 Animal의 prototype영역을 검사하는 것일뿐 dog.legsCount가 있다면 Animal의 prototype은 상관없게된다.

__proto__ 는 부모객체의 prototype 영역을 가리킨다.


따라서 dog.__proto__ 를 사용해서 부모 객체의 프로토타입에 접근할 수 있다. 물론 데이터의 변경도 가능하다.

dog.__proto__.legsCount = 5;

console.log(Animal.prototype.legsCount);
> 5

프로토를 활용하면 새로운 방법으로 객체의 상속을 만들어낼 수 있다.

function Animal() {}
var dog = {};
dog.__proto__ = new Animal();

하지만 이제 __proto__ 의 사용은 권장하고 있지 않다.
대신 Object.getPrototypeOf(obj) 와 Object.setPrototypeOf(obj) 를 사용해 부모객체의 프로토타입에 대한 접근을 권장하고 있는데 __proto__ 를 사용해 직접적인 접근은 위험하기 때문이다.

  1. Object 객체

Object 객체는 최상위 객체이다. 모든 객체는 Object를 상속하고 있다. 이 말은 모든 객체의 proto 혹은 constructor, prototype-chaining 을 통해 Object 객체의 프로토타입에 접근할 수 있음을 의미한다.
Object의 prototype에는 어떤 속성들이 있을까?

이 속성들은 dog 객체에서도 prototype-chaining 을 통해 사용이 가능하다.

The End


Javascript 를 좋아하고 자주 사용했지만 기본적인 개념에 대해 막연하게 알고 있을 뿐 누군가에게 설명해보라고 하면 벙어리가 될 것 같아 한번 나만의 글로 정리를 해봤다.

프로토타입 기반 프로그래밍이란 무엇인가요?

쉽게 말해 상속의 개념을 구현한 것입니다. A객체와 B객체가 있을 때 B객체가 A객체를 상속받고 있다고 하면 A객체는 B객체의 프로토타입을 참조하고 있습니다. 따라서 B객체의 프로토타입에 속성을 정의하면 프로토타입 체이닝을 통해 A객체도 같은 속성을 가질 수 있습니다.