[JS 프로토타입] 자바스크립트의 프로토타입 훑어보기

[JS 프로토타입] 자바스크립트의 프로토타입 훑어보기


이번 포스팅에서는 자바스크립트(JavaScript)하면 빠질 수 없는 프로토타입(Prototype)에 대해서 한번 이야기해보려고 한다.

프로토타입은 자바스크립트를 ES5 시절부터 사용해오던 분들에게는 매우 익숙하지만 ES6부터 시작하신 분들은 대부분 클래스를 사용하기 때문에 익숙한 개념은 아닐 것이라고 생각한다.

필자가 처음 프론트엔드 개발을 시작했을때는 자바스크립트의 ES5 버전에서 막 ES6로 넘어가고 있던 시절이었는데, 기존에는 자바(Java)를 주로 사용하고 있던 필자가 프론트엔드 개발로 넘어오면서 제일 애먹었던 부분이 바로 이 프로토타입이었다.(물론 애먹는 건 현재진행형이다)

물론 지금은 자바스크립트의 위상이 많이 올라가면서 프로토타입 패턴에 대한 관심도 많아지기 시작했지만, 그래도 당시나 지금이나 여전히 주류는 C 계열 언어나 Java에서 사용하는 클래스를 기반으로한 객체 생성 방식이다.

그래서 자바스크립트를 처음 접하는 개발자에게 프로토타입 기반 프로그래밍은 상대적으로 낯선 방식일 수 밖에 없고, 이로 인해 기존 개발자들이 자바스크립트로 진입하는데 어려움이 있었다. 그런 이유로 ES6에서는 class 예약어가 등장한 것이다.

사실 필자도 아직 클래스 기반의 객체 생성 방식이 익숙하기 때문에 프로토타입에 대한 공부가 더 필요하다.

그래서 이번 포스팅에서는 프로토타입 패턴이 무엇인지, 자바스크립트 내에서 프로토타입이 어떤 방식으로 사용되고 있는지에 집중해서 한번 이야기해보려고 한다.

ES6부터 클래스를 지원하는데도 프로토타입을 굳이 알아야 하나요?

자바스크립트는 ES6부터 class 키워드를 사용하여 클래스를 지원하고 있다. 정확히 말하면 프로토타입으로 클래스를 흉내내서 구현한 것이라고 말하는 것이 맞다.

그런 이유로 많은 개발자들이 자바스크립트의 클래스를 단순한 문법 설탕(Syntactic Sugar)라고 이야기하지만, 사실 개인적으로 자바스크립트의 클래스는 ES5 시절 프로토타입을 사용하여 객체를 생성했던 방법보다 더 엄격한 제약을 가지고 있기 때문에 단순한 문법 설탕이라기보다는 상위 요소(Superset)라고 하는게 맞지 않나 싶다.

그러면 그냥 클래스를 쓰면 되는데 왜 프로토타입을 알아야 하는 것일까?

그 이유는 ES6에서 class 키워드를 통해 클래스를 지원하고 있기는 하지만, 이건 자바스크립트가 클래스 기반 언어가 되었다는 의미는 아니기 때문이다. 결국 자바스크립트 안에서의 클래스는 클래스의 탈을 쓴 프로토타입이다.

그리고 예전에 작성된 레거시 프론트엔드 코드의 경우에는 ES5로 작성된 것도 많기 때문에 아직까지 프론트엔드 개발자들은 ES5를 만져야하는 경우가 왕왕 있는 것이 현실이다. 물론 ES5를 ES6 이상의 버전으로 마이그레이션하려고 해도 기존의 프로토타입 기반의 객체 생성이나 상속이 구현된 코드를 이해할 수 없다면 마이그레이션 또한 불가능하다.

프로토타입은 디자인 패턴이다

프로토타입이라고 하면 일반적으로 자바스크립트를 떠올리지만, 사실 프로토타입은 자바스크립트에서만 사용되는 것은 아니고, 그냥 일종의 디자인 패턴 중 하나이다. 자바스크립트 뿐만 아니라 ActionScript, Lua, Perl 등 프로토타입 기반 프로그래밍을 지원하는 다른 언어도 많다.

그래서 자바스크립트의 프로토타입을 자세히 알아보기 전에 디자인 패턴으로써의 프로토타입을 먼저 알아볼까 한다.

프로토타입 패턴은 객체를 효율적으로 생성하는 방법을 다루는 패턴 중 하나인데, 주로 객체를 생성하는 비용이 클 때 이를 회피하기 위해 사용된다.

객체를 생성할 때의 비용이 크다는 말은, 말 그대로 객체를 생성할 때마다 뭔가 일을 많이 해야한다는 뜻이다.

예를 들어 RPG 게임의 캐릭터를 하나 구현해본다고 생각해보자. 이 캐릭터는 여러가지 장비를 장착할 수 있는 기능을 가지고 있는데, 처음 캐릭터가 생성될 때 딸랑 맨 몸으로 시작하면 유저들이 싫어할 것 같으니 기본적인 장비 몇 가지를 장착한 상태로 생성될 수 있도록 만들어주려고 한다.

Player.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Weapon {}
class Armor {}
class BasicSward extends Weapon {}
class BasicArmor extends Armor {}

class Player {
public Weapon weapon;
public Armor armor;

public Player() {
this.weapon = new BasicSward(); // 초심자의 목도
this.armor = new BasicArmor(); // 초보자용 갑주
}
}

간단하게 만들어보면 대충 이런 느낌이다. Player 객체는 자신이 생성될 때 BasicSward 객체와 BasicArmor 객체까지 함께 생성해야한다.

이런 경우 그냥 Player 객체만 생성하는 상황보다는 객체의 생성 비용이 높다고 할 수 있다. 게다가 캐릭터 생성 시 처음 부여하는 아이템의 종류가 많아질수록 Player의 객체의 생성 비용 또한 계속 높아질 것이다.

음… 근데 곰곰히 생각해보니 캐릭터가 처음 생성되며 가지고 있는 아이템이 항상 같다는 전제 조건이 있다면 생성 비용이 높은 Player객체를 딱 한번만 생성하고 그 다음부터는 생성된 객체를 복사해서 사용해도 될 것 같다는 생각이 든다.

1
2
3
4
5
6
7
8
9
10
// 이건 너무 객체 생성 비용이 높으니까...
Player evan = new Player();
Player john = new Player();
Player wilson = new Player();

// 이런 방법으로 접근해보는 것은 어떨까?
Player player = new Player();
Player evan = player.clone();
Player john = player.clone();
Player wilson = player.clone();

이런 관점으로 접근하는 것이 바로 프로토타입 패턴이라고 할 수 있다. 프로토타입, 즉 원본 객체가 존재하고 그 객체를 복제해서 새로운 객체를 생성하는 방법인 것이다.

실제로 자바에서 프로토타입 패턴을 사용할때, 복제 대상이 되는 클래스는 보통 Cloneable 인터페이스를 사용하여 구현한다. Cloneable 인터페이스에는 clone 메소드가 정의되어 있기 때문에, 이 인터페이스를 사용하는 클래스는 반드시 clone 메소드를 오버라이딩해서 구현해야한다.

1
2
3
4
5
6
7
class Player implements Cloneable {
//...
@Override
public Player clone () throws CloneNotSupportedException {
return (Player)super.clone();
}
}

clone 메소드를 구현하고나면 이제 Player 객체는 복사 가능한 객체가 된다. 즉, 다른 객체들의 원본 객체가 될 수 있는 기능을 가지게 되었다는 것이다.

이제부터는 Player 객체를 추가로 생성하고 싶을 때는 기존에 생성되어 있던 객체를 그대로 복사하면 되기 때문에 높은 객체 생성 비용이 드는 것을 피할 수 있다.

1
2
Player evan = new Player();
Player evanClone = evan.clone();

또한 Player 객체는 복사되어 새로운 메모리 공간을 할당받지만, 깊은 복사를 하지 않는 이상 Player객체가 가지고 있는 BasicSward 객체와 BasicArmor 객체는 새롭게 생성되지 않고 기존에 이 객체들이 할당된 메모리 공간을 참조하기만 한다.

즉, 잘만 쓴다면 메모리 공간을 아낄 수도 있다는 것이다. 자바스크립트에서 원시 자료형은 Call by value, 그 외 자료형은 Call by reference를 사용하는 것과 동일한 원리이다.

여기까지 듣고 나서 예상하신 분들도 있겠지만, 그 말인 즉슨 잠깐 정신줄 놓고 코딩하다보면 이런 슬픈 상황도 발생할 수 있다는 뜻이다.

1
2
3
4
5
6
7
8
9
10
11
Player evan = new Player();
try {
Player evanClone = evan.clone();
evanClone.weapon.attackPoint = 40;

System.out.println("에반 무기 공격력 -> " + evan.weapon.attackPoint);
System.out.println("에반 복사본 무기 공격력 -> " + evanClone.weapon.attackPoint);
}
catch (Exception e) {
System.err.println(e);
}
1
2
에반 무기 공격력 -> 40
에반 복사본 무기 공격력 -> 40


디버깅 지옥이 펼쳐진다…


정리해보자면 프로토타입 패턴이란, 객체를 생성할 때 원본이 되는 객체를 복사해서 생성하는 패턴이라고 할 수 있다.

물론 자바스크립트의 프로토타입은 단순히 몇 개의 객체가 복제 관계를 가지는 것이 아니라, 자바스크립트 내의 모든 객체 전체가 복제 관계로 얽혀있기 때문에 이것보다는 약간 더 복잡하긴 하지만, 근본적인 원리 자체는 프로토타입 패턴을 따라간다.

그럼 이제 자바스크립트가 객체를 생성할 때 프로토타입 패턴을 어떤 식으로 사용하고 있는 지 한번 알아보도록 하자.

자바스크립트의 프로토타입

앞서 설명했듯이 프로토타입 패턴은 객체를 생성할 때 사용하는 패턴이다. 필자가 위에서 예시로 사용한 언어인 자바는 클래스 기반 프로그래밍을 지원하기 때문에, 특수한 패턴을 사용해야지만 프로토타입이라는 개념을 사용할 수 있다.

그러나 애초에 프로토타입 기반 프로그래밍을 지원하는 자바스크립트의 경우에는 애초에 모든 객체를 생성할 때 프로토타입을 사용하기 때문에, 객체를 생성하기만 해도 위에서 필자가 설명한 프로토타입 패턴이 적용된다.

그렇기 때문에 우선 자바스크립트에서 말하는 객체(Object)가 무엇인지, 그리고 그 객체가 생성된다는 것이 무엇을 의미하는 것인지 알아볼 필요가 있다.

자바스크립트가 객체를 생성하는 방법

컴퓨터 공학에서의 객체(Object)현실의 사물을 프로그램에 반영한 것이다. 즉, 여러 개의 프로퍼티(특징)와 메소드(행위)를 가지고 현실의 사물을 흉내내는 존재인 것이다.

클래스 기반 언어에서는 클래스를 생성하고 그 클래스를 사용하여 객체를 생성해야하지만, 자바스크립트는 간단한 문법만으로 객체를 생성할 수 있다.

1
2
3
4
5
6
7
const evan = {
name: 'Evan',
age: 29,
say: function () {
console.log(`Hi, I am ${this.name}!`);
}
};

이런 방식을 우리는 리터럴(Literal)로 객체를 선언한다고 한다. 리터럴은 소스 코드의 고정된 값을 대표하는 일종의 단축어 같은 개념이기 때문에, 우리는 간단한 문법만으로 객체를 생성했다고 느끼지만 내부적으로는 객체를 생성하는 일련의 매커니즘이 작동하고 있다.

예를 들어, 다른 언어에서는 이런 리터럴 문법을 사용하여 객체를 생성할 때 내부적으로 클래스를 사용하게된다. 파이썬 같은 경우, 딕셔너리를 리터럴로 선언하고 타입을 찍어보면 dict 클래스가 출력되는 것을 볼 수 있다.

1
2
3
4
5
my_dict = {
'name': 'Evan',
'age': 29
}
type(my_dict)
1
<class 'dict'>

우리는 dict({ 'name': 'Evan', 'age': 29 })와 같이 클래스를 명시적으로 사용하지않고 리터럴로 딕셔너리를 생성했지만 내부적으로는 제대로 dict 클래스를 사용해서 객체를 생성했다는 것이다.

자바 또한 리터럴 문법을 지원하는 배열(Array)을 선언한 후 출력해보면 결국 클래스를 기반으로 배열 객체를 생성한다는 것을 알 수 있다.

1
2
String[] array = {"Evan", "29"};
System.out.println(array);
1
[Ljava.lang.String;@7852e922

이 말인 즉슨, 다른 언어와 마찬가지로 자바스크립트의 객체도 갑자기 혼자서 뿅 하고 생성되는 것이 아니라 분명히 뭔가를 사용해서 만들어내고 있다는 말이다.

하지만 자바스크립트에는 클래스라는 개념 자체가 없는데 뭘 사용해서 객체를 만들어내고 있는 것일까?

답은 바로 함수(Function)이다.

자바스크립트에서 객체가 생성되는 원리를 조금 더 파헤쳐보기 위해서 위에서 리터럴로 선언했던 evan 객체를 이번에는 다른 방법으로 선언해보도록 하겠다.

1
2
3
4
const evan = new Object({
name: 'Evan',
age: 29,
});

왠지 클래스 기반 언어에서 클래스를 사용하여 객체를 생성하는 것과 유사한 문법이 나타났다. 이런 방식을 생성자(Constructor)를 사용하여 객체를 생성한다고 한다.

클래스 기반 언어라면 Object는 클래스겠지만, 자바스크립트에서는 클래스가 아닌 함수이다.

즉, 자바스크립트에서의 생성자는 함수가 가지고 있다는 것이다. 저게 진짜 함수인지 알고 싶으니, 브라우저 콘솔 창을 열고 Object를 한번 출력해보도록 하겠다.

1
2
console.log(Object);
console.log(typeof Object);
1
2
ƒ Object() { [native code] }
"function"

음, 콘솔로 찍어보니 Object는 확실하게 빼박캔트 함수가 맞다.

필자가 처음 자바스크립트를 사용하기 시작했을 때 받아들이기 어려웠던 부분이 바로 이 부분이었다.

클래스 기반 프로그래밍에 익숙했던 필자에게 new 키워드와 생성자는 클래스만 가질 수 있는 것이었는데 갑자기 뜬금없이 함수가 나와버리니 받아들이기 힘들었던 것 같다.(머리로는 알겠는데 마음이…)

어쨌든 이제 자바스크립트가 객체를 생성할 때 함수를 사용해서 생성한다는 것을 알게되었다. 지금까지 알아낸 내용을 정리해보자면 다음과 같다.

  1. 프로토타입 패턴이란 객체를 생성할 때 원본 객체를 복제하여 생성하는 방법이다.
  2. 자바스크립트는 객체를 생성할 때 프로토타입 패턴을 사용한다.
  3. 자바스크립트는 객체를 생성할 때 함수를 사용한다.

그렇다는 것은 자바스크립트가 함수를 사용하여 객체를 생성할 때 뭔가를 참조하고 복제해서 객체를 생성한다는 말이다. 이제부터 그 뭔가를 알아 볼 시간이다.

도대체 뭘 복제해서 객체를 만드는 걸까?

사실 디자인 패턴으로써의 프로토타입 패턴은 생각보다 그렇게 어렵지 않다. 그저 객체를 생성할 때 원본 객체를 복제해서 생성한다는 개념이기 때문이다.

마찬가지로 자바스크립트 또한 뭔가를 복제해서 새로운 객체를 생성하고 있다. 그럼 이제 자바스크립트가 도대체 뭘 복제해서 객체를 생성하고 있는 것인지 알아보기 위해 간단한 함수를 하나 선언해보도록 하겠다.

1
2
3
4
5
6
function User () {}

const evan = new User();

console.log(evan);
console.log(typeof evan);
1
2
User { __proto__: Object }
object

위에서 이야기했듯이 자바스크립트는 함수를 사용하여 객체를 생성하기 때문에, 이렇게 클래스를 사용하는 것과 유사한 느낌으로 객체를 생성할 수 있다.

그렇다면 evan 객체는 무엇으로부터 복제된 것일까? 간단하게 생각하면 User 함수라고 생각해볼수 있겠지만, 사실은 User 함수를 복제한 것이 아니라 User 함수의 프로토타입 객체를 복제한 것이다.



이렇게 갑자기 프로토타입이 나온다고…?


뜬금없어서 바로 이해가 안될 수도 있겠지만, 단순하게 생각해보면 쉽다. 만약 객체를 생성하면서 함수를 복제했다면 생성된 객체는 object 타입이 아니라 function 타입이어야 하지 않겠는가?

하지만 evan 객체는 object 타입을 가지고 있다. 즉, 이 함수 자체가 아니라 다른 객체 타입의 무언가를 복제했다는 것이고, 그 원본 객체가 User 함수의 프로토타입 객체인 것이다.

필자는 User 함수의 프로토타입을 명시적으로 선언하지 않았지만, 자바스크립트는 함수가 생성될 때 자동으로 그 함수의 프로토타입 객체(Prototype Object)도 함께 생성하고 해당 함수의 prototype 프로퍼티에 연결해둔다.

1
2
3
4
function User () {}

console.log(User.prototype);
console.log(typeof User.prototype);
1
2
{ constructor: f User(), __proto__: Object }
object

분명히 필자는 함수만 선언했는데, User.prototype 프로퍼티에 뭔가 이것저것 가지고 있는 객체 녀석이 1+1으로 붙어나왔다. 함수를 생성하면 무조건 그 함수의 프로토타입 객체도 함께 생성된다는 것이 키포인트다.

그리고 이 프로토타입 객체는 함수를 사용해서 새로운 객체를 생성할 때 원본 객체 역할을 해줄 객체를 의미한다.

즉, new User()라는 문법을 사용하여 새로운 객체를 만들게 되면 User 함수 자체가 아니라 User 함수가 생성될 때 함께 생성된 User 함수의 프로토타입 객체를 복제해서 새로운 객체를 만든다는 것이다.



evan 객체야, 내가 아니라 내 프로토타입 객체를 복제하렴


이때 User 함수가 생성되며 함께 생성된 User 함수의 프로토타입 객체를 프로토타입 프로퍼티(Prototype Property)라고 한다.

그럼 이 프로토타입 객체가 가지고 있는 프로퍼티인 constructor__proto__는 뭘 의미하는 걸까?

constructor

함수가 생성되며 함께 생성된 프로토타입 객체는 모두 constructor라는 프로퍼티를 가지고 있다. 그리고 이 프로퍼티에는 이 프로토타입 객체가 생성될 때 선언했던 함수가 들어있다.

1
console.log(User.prototype);
1
2
3
4
{
constructor: f User(),
__proto__: Object
}

함수를 선언하면 함수와 함께 해당 함수의 프로토타입 객체도 함께 생성되며 이 둘을 연결하게 된다. 이때 함수는 프로토타입 객체의 constructor 프로퍼티로 연결되고, 프로토타입 객체는 함수의 prototype 프로퍼티로 연결되는 것이다.



함수와 프로토타입 객체는 서로 연결되어있다


1
console.log(User.prototype.constructor === User);
1
true

이 생성자 프로퍼티는 이 함수를 통해 생성된 객체 입장에서 보았을 때 나를 만들 때 어떤 함수가 호출되었냐?를 의미한다. 만약 이 연결이 없다면 새로 생성된 객체는 자신을 만들 때 어떤 생성자 함수가 호출되었는지 알 수가 없다.

새롭게 생성된 객체는 자신을 생성할 때 어떤 원본 객체를 복사했는지에 대한 링크는 가지고 있지만 어떤 생성자가 호출되었는지에 대한 링크는 가지고 있지 않기 때문이다.

하지만 원본 객체의 constuctor 프로퍼티에 생성자 함수가 연결되어있기 때문에 새롭게 만들어진 객체는 자신의 원본 객체에 접근해서 이 프로퍼티를 참조함으로써 자신이 만들어질때 어떤 생성자 함수가 호출되었는지를 알 수 있다.

1
2
const evan = new User();
console.log(evan.__proto__.constructor === User);
1
true

이때 생성된 객체가 자신의 원본 객체에 접근할 수 있는 프로퍼티가 바로 __proto__ 프로퍼티이다.

__proto__

방금 생성자를 설명하면서 함수를 통해 새롭게 생성된 객체는 원본 객체와의 연결을 가지고 있다고 했다. 이때 이 연결을 프로토타입 링크(Prototype Link)라고 한다.

Object.prototype을 제외한 자바스크립트 내의 모든 객체는 원본 객체를 기반으로 복사되어 생성되었기 때문에, 자신의 원본 객체로 연결되어있는 프로토타입 링크 또한 모든 객체가 가지고 있다. 이때 이 링크가 담기는 프로퍼티가 __proto__ 프로퍼티이다.

Object.prototype.__proto__가 존재하지 않는 이유는 밑에서 후술하도록 하겠다. 우선은 객체들이 자신의 원본 객체로 통하는 프로토타입 링크를 가지고 있다는 사실에만 집중하자.

이 포스팅에서는 이해를 돕기위해 __proto__ 프로퍼티를 그대로 사용하고 있다.
그러나 해당 프로퍼티는 ECMAScript 2015에서는 표준이었지만 현재는 표준이 아니므로 Object.getPrototypeOf()를 사용하는 것을 추천한다.

User 함수를 사용하여 생성한 객체는 User.prototype 객체를 복사하여 생성된 객체이기 때문에, 이 객체들은 원본인 User.prototype 객체를 자신의 __proto__ 프로퍼티에 연결해두는 것이다.

1
2
3
function User () {}
const evan = new User();
console.log(evan.__proto__ === User.prototype);
1
true

그렇다면 이 프로토타입 링크를 사용해서 계속 해서 원본 객체를 추적하다보면, 결국은 자바스크립트 내의 모든 객체들이 최종적으로 어떤 원본 객체를 복사해서 생성된 것인지 알 수 있지 않을까?

프로토타입 체인

자바스크립트 내의 사용되는 모든 객체들은 전부 이런 프로토타입 기반 방식으로 정의되고 생성된다. 즉, String, Boolean, Array와 같이 우리가 일반적으로 사용하고 있는 빌트인 객체들도 모두 같은 방식을 사용해서 만들었다는 것이다.

그렇다면 이 객체들은 어떤 프로토타입 객체를 복사해서 만들어진 것일까?

String, Boolean, Array든 뭐가 됐든 자바스크립트 내에 존재하는 모든 것들은 바로 Object 함수의 프로토타입인 Object.prototype을 시작으로 해서 복제된다.

위에서 __proto__를 설명하면서 Object.prototype 객체는 프로토타입 링크, 즉 원본 객체로 통하는 링크가 없다고 이야기했었는데, 그 이유는 바로 Object.prototype이 모든 객체들의 조상님이기 때문이다.

이게 정말인지 확인해보고 싶다면, 아무 객체나 골라잡아서 그 객체의 __proto__ 프로퍼티를 통해 쭉쭉 올라가보면 된다.

일단 만만한 String을 사용해서 조상을 추적해보려고 하는데, String 객체를 생성하는 함수부터 출발할 것인지, String 함수를 통해 생성된 객체부터 출발할 것인지에 따라 조상까지 올라가는 길이 달라진다.

String 함수와 String 객체는 당연히 원본이 되는 객체도 다르기 때문이다. 필자는 그 중 String 객체를 생성할 수 있는 String 생성자 함수를 선택했다.

1
2
3
4
5
const first = String.__proto__;
const second = first.__proto__;

console.log('첫번째 조상 -> ', first.constructor.name);
console.log('두번째 조상 -> ', second.constructor.name);
1
2
첫 번째 조상 -> Function
두 번째 조상 -> Object

자바스크립트의 모든 함수는 자신의 원본으로 Function.prototype 객체를 원본으로 가진다. 그리고 Function.prototype은 결국 객체이기 때문에, 당연히 원본으로 Object.prototype 객체를 원본으로 가진다.

그럼 여기서 한번 더 올라가면 어떻게 될까?

1
2
const third = second.__proto__;
console.log(third.constructor.name);
1
Uncaught TypeError: Cannot read property 'constructor' of null at <anonymous>:1:28

앗, TypeError가 발생했다. 에러메세지를 보아하니 Object.prototype 객체의 원본 객체인 Object.prototype.__proto__null인 모양이다.

즉, Object의 위로는 더 이상 조상이 없는 것이다. 지금 살펴본 이 관계를 간단한 다이어그램으로 나타내어보면 다음과 같다.





뭔가 복잡해보이지만 별 것 없다. String 함수의 원본 객체는 Function.prototype이다. 그리고 const a = 'evan'과 같이 선언된 String 객체는 자신을 생성한 String 함수의 String.prototype을 원본으로 가질 것이고, String.prototype은 객체이기 때문에 당연히 Object.prototype을 원본으로 가지는 것이다.

이렇게 프로토타입으로 이루어진 객체들의 관계를 프로토타입 체인(Prototype Chain)이라고 한다.

마치며

사실 필자가 프로토타입에 대한 포스팅을 쓰려고 했던 이유는 얼마 전 면접에서 자바스크립트의 프로토타입을 사용하여 Private Static 메소드를 구현하라는 문제를 받았다가 결국 못 풀었기 때문이다.

자바스크립트의 클로저와 프로토타입을 활용하여 풀어야하는 문제였는데, 필자는 기본기가 부족한 나머지 풀어내지 못했다.

그래서 원래는 프로토타입을 사용한 다양한 상속 기법들과 클로저를 사용한 멤버의 은닉 등도 함께 소개해보려고 했지만, 늘 그렇듯 분량 조절 실패로 인해 다른 포스팅에서 별도로 다뤄야할 것 같다.



사실 애초에 분량 조절 따위를 생각하고 쓰지 않는다


필자처럼 기존의 클래스 기반 객체 생성방식에 익숙한 개발자들에게 자바스크립트의 프로토타입은 꽤나 복잡하게 느껴진다. 디자인 패턴으로써의 프로토타입은 단순히 객체를 복제해서 새로운 객체를 생성한다는 정도의 개념에 그치지만 자바스크립트의 프로토타입 체인은 그것보다 훨씬 더 복잡하게 연결되어있기 때문이다.

하지만 프로토타입 체인이나 프로토타입을 사용한 각종 상속 기법은 어렵게 느껴질 수 있어도, 프로토타입의 뼈대 자체는 그렇게 어렵지 않다고 생각한다.

  1. 객체는 함수를 사용해서 만들어지고, 객체는 함수의 프로토타입 객체를 복제하여 생성된다.
  2. 모든 객체는 자신이 어떤 원본 객체를 복제하여 생성된 것인지에 대한 정보를 가지고 있다.

물론 원본 객체에 대한 정보를 런타임에 동적으로 변경할 수 있는 등 변태같은 짓들이 가능하기도 하고, 이를 사용한 다양한 기법들도 있기는 하지만 기본은 결국 저 두 가지라고 할 수 있을 것 같다. 다음 포스팅에서는 본격적으로 프로토타입을 사용한 상속 기법과 객체의 프로퍼티를 탐색하는 방법인 프로토타입 룩업 등에 대해서 설명하도록 하겠다.

이상으로 자바스크립트의 프로토타입 훑어보기 포스팅을 마친다.

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×