태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.


* 이번에는 자바스크립트에서 객체지향을 기본적으로 구현하기 위한 개념인 prototype에 대하여 공부해보도록 하자. 이 개념은 단순한 자바스크립트 처리에서는 중요하지 않고 모르고 넘어간 경우가 많이 있었을테지만, 조금 복잡한 자바스크립트 또는 자바스크립트 라이브러리를 만드는데 있어서 객체 지향적인 개념을 활용하고자할 때 유용하게 사용할 수 있어서 알아두면 좋은 개념이다. 특히, 자바스크립트가 내부적으로 어떻게 동작하는지에 대하여 조금 더 이해를 하기 위하여 반드시 알아두고 넘어가자. 이번에는 약간 하단에서 돌아가는 방식에 대하여 알아볼 것이고 다음 글에서는 실제로 활용하는 방법과 상속에 대하여 알아볼 것인데, 이번 글에서 아주 깊숙한 내용까지 다룰 것이기 때문에 지금은 겉으로만 객체를 생성했을 때 일어나는 일들과 prototype을 알았더라도 읽고나면 그 내부까지 거의다 이해할 수 있으리라고 생각한다.



- 이전 글

2012/12/10 - [속깊은 자바스크립트 강좌] 시작 (예고편)

2012/12/17 - [속깊은 자바스크립트 강좌] 자바스크립트의 Scope와 Closure 기초

2013/01/07 - [속깊은 자바스크립트 강좌] function declaration vs function expression 차이점

2013/01/10 - [속깊은 자바스크립트 강좌] 함수를 호출하는 방법과 this의 이해

2013/01/21 - [속깊은 자바스크립트 강좌] Closure의 이해 / 오버로딩 구현하기

2013/01/30 - [속깊은 자바스크립트 강좌] Closure 쉽게 이해하기/실용 예제 소스

2013/02/13 - [속깊은 자바스크립트 강좌] 쉬어가기: 웹 개발 방법론의 변화/자바스크립트의 재발견



* prototype?

: 자바스크립트를 깊이있게 다루지 못했을 때에 처음으로 prototype이라는 말을 접하면 이것이 뭔가 하는 생각이 들기도 하고, 용도가 무엇이고 어떻게 써야할지 몰라서 방황할지도 모르고 심지어 prototype을 대충 접해보고는 처음부터 없었던 것처럼 다시 원래의 개발하던 방법으로 돌아갈지도 모른다. 사실 간단한 기능을 위하여 사용하는 자바스크립트에서는 prototype 개념을 알 필요가 별로 없지만, 객체지향을 선호하는 개발자들이라면, 또는 라이브러리를 통해 다양한 인스턴스들을 생성하고자하는 라이브러리 개발자라면, 그리고 자바스크립트를 조금 더 이해하고자 한다면 반드시 알아야할 개념이다. 일단 prototype의 사전적 의미를 보면, '원형'이라는 뜻을 가지고 있는데, 이 말 그대로 받아들이면 된다. 하지만 무엇의 원형인지를 생각하면서부터 다소 갸우뚱하게 된다. 따라서 먼저 객체에 대하여 알아보고 넘어가야할 것이다.



* 객체(object) 선언하고 생성하기

: 많은 웹 개발자들은 자바스크립트에서 객체를 선언하고 생성할 수 있다는 점을 모르고 있기도 하지만, 처음부터 자바의 기능들을 조금 가져오려고 했었기 때문에 그러한 기능들이 일부 포함되고 있고, 그 중 객체를 생성하는 부분 또한 포함되어있다. 하지만 핵심적인 다른 점이 있다면, 자바스크립트는 함수를 기반으로한 언어이기 때문에 객체의 선언은 함수를 통해서 하게 되는 것이다.


function Person(name, blog) {
    this.name = name;
    this.blog = blog;
}
var unikys = new Person("unikys", "unikys.tistory.com");
alert(unikys.name); // === "unikys"


- 덧: 자바스크립트에서는 개발자들 사이에서 명명 규칙으로 정하기를 함수의 첫 글자를 대문자로 (위에서는 Person의 P 대문자) 설정하게 되면, 해당하는 함수는 객체를 선언한 것임을 명시하는 전통적인 암묵적인 약속이 있다. 따라서, 객체를 선언하는 함수라면 첫글자는 대문자로 하고 만약 일반 함수라면 첫글자는 소문자로 하는 것을 습관화 들이면 좋을 것이다.


: 위의 내용은 그렇게 어려운 내용은 아니다. 그냥 기분상 class를 function으로 설정한듯한 기분이 드니까 그럴 것인데, 자바스크립트에서 new를 사용하게 되면 다른 과정들을 한번 깊게 살펴보자.



* new 연산자

:  자바나 C++에서 많이 쓰이는 new는 메모리를 새롭게 할당해서 해당하는 클래스의 생성자를 호출하면서 인스턴스를 초기화하고, 그 인스턴스를 생성하여 리턴하는 과정이 일어나게 되는데, 자바스크립트에서는 상당히 비슷하지만 조금은 다르게 받아들여야할 것이다. 왜냐하면 C++이나 자바에서는 new 뒤에 바로 class를 사용하지만 자바스크립트에서는 new 뒤에 바로 함수를 파라미터로 활용하기 때문이다. 함수를 바로 사용하기 때문에 이 함수는 생성하는 객체의 constructor(생성자)가 되는 것이고, 초기화도 바로 이 함수를 통해서 일어나게 되는데, 위의 함수에서도 사용했지만, 이전의 강좌 중에서 다뤘던 내용 중 "this"가 결정되는 방법에 대하여 다룬 적이 있다. 사실 다른 언어처럼 class로 되어있다면 그렇게 고민을 할 필요도 없겠지만, 자바스크립트는 function을 똑같이 객체의 생성자로서 활용하기 때문에 이전에 배웠던 this가 결정되는 방법과 위의 함수에서 this를 이용하는 것만 보면 얼핏 글로벌 변수(window)에다가 추가하는 듯한 기분이 들기도 하지만 뭔가는 다를 것 같은 기분이 들 것이다. 이럴 때에는 조금 더 깊이 있게 내부적으로 어떠한 단계들이 일어나는지 살펴보면 좋다. ECMAScript 5.1의 스펙을 살펴보자.


http://www.ecma-international.org/ecma-262/5.1/#sec-13.2.2


: 위의 링크에 들어가면 객체가 생성될 때 일어나는 단계들을 설명하고 있다. 그 단계는 바로 아래와 같다.


When the [[Construct]] internal method for a Function object F is called with a possibly empty list of arguments, the following steps are taken:

1. Let obj be a newly created native ECMAScript object.

2. Set all the internal methods of obj as specified in 8.12.

3. Set the [[Class]] internal property of obj to "Object".

4. Set the [[Extensible]] internal property of obj to true.

5. Let proto be the value of calling the [[Get]] internal property of F with argument "prototype".

6. If Type(proto) is Object, set the [[Prototype]] internal property of obj to proto.

7. If Type(proto) is not Object, set the [[Prototype]] internal property of obj to the standard built-in Object prototype object as described in 15.2.4.

8. Let result be the result of calling the [[Call]] internal property of F, providing obj as the this value and providing the argument list passed into [[Construct]] as args.

9. If Type(result) is Object then return result.

10. Return obj.


: 위의 단계들을 순서대로 보면서 파악해도 되지만, 제일 중요한 부분을 살펴보면 바로 8번이다. 여기서 명시되어있기로는, obj는 this 값으로 설정한다고 되어있고, 이 obj는 1번에서 기본 object를 생성하는 것이라고 되어있다. 그리고 2번~7번까지는 이 obj에 다양한 속성들을 꾸며주는 것을 볼 수 있고 마지막 10번에는 이렇게 생성되고 꾸며진 obj를 리턴하여 new 연산자의 결과 값으로 설정되는 것을 알 수 있다. 따라서 생성자 내부에서는 this는 새로 생성된 obj로 설정되며, 이 this에는 위의 예처럼 this.name등 여러 가지 속성들을 추가하게 되면 생성되는 객체에 이러한 속성들이 추가되서 리턴하게 되는 것이다. 



* 그럼 prototype은 어디에 붙어있나?

: 위의 객체가 생성되는 단계 중에서 5번과 6번 단계를 살펴보면 "prototype"이라 속성이 나온다. 여기서 5단계를 해석해보면 F의 prototype 속성을 GET해서 설정한다고 나와있는데, 여기서 말하는 F라 하면 1단계 위의 설명에서 "a Function object F"가 생성자로 호출될 때라는 말이 있다. 따라서 이 prototype은 '생성자 함수 F'의 속성으로 설정된다는 것을 알 수 있다. 그럼 이제는 사전적의미의 prototype이 아닌 ECMAScript에서 정의하고 있는 prototype을 살펴보자. 아래의 링크는 constructor로 이어졌지만 그 바로 아래에 prototype이 써있다.


http://www.ecma-international.org/ecma-262/5.1/#sec-4.3.4


prototype: object that provides shared properties for other objects

NOTE- When a constructor creates an object, that object implicitly references the constructor’s “prototype” property for the purpose of resolving property references. The constructor’s “prototype” property can be referenced by the program expression constructor.prototype, and properties added to an object’s prototype are shared, through inheritance, by all objects sharing the prototype. Alternatively, a new object may be created with an explicitly specified prototype by using the Object.create built-in function.


: ECMAScript에서 정의하고 있는 prototype이란 다른 객체들과 공유된 속성을 제공하는 객체라고 써있다. prototype이 하나의 객체라는 점을 주목하고 넘어가자. Note의 내용은 잠시 뒤에 다시 돌아와서 살펴보고, 일단 이제 prototype이 어디에 붙어있는지 알았으므로 실제로 활용하는 예를 '드디어' 한번 살펴보자. (prototype까지 오기가 너무 오래걸린것 같다 ㅠ)



* prototype 예

: 서론이 참 길었던 prototype 이론적인 이야기는 이제 마치고 실제로 예를 살펴보자. 위의 예에서 이어서 한번 만들어보자. 위에서 생성자 함수 F의 속성 prototype을 가져와서 새로운 객체에 추가한다고 되어있으므로, 위의 예에서 쓴 Person 함수에 prototype의 속성을 새로운 객체로 설정해보자.


function Person(name, blog) {
    this.name = name;
    this.blog = blog;
}
Person.prototype = {
    getName: function () {
        return this.name;
    },
    getBlog: function () {
        return this.blog;
    }
};
var unikys = new Person("unikys", "unikys.tistory.com");
var stranger = new Person("stranger", "www.google.com");
alert(unikys.getName()); // === "unikys"
alert(unikys.getBlog()); // === "unikys.tistory.com"
alert(stranger.getName()); // === "stranger"
alert(stranger.getBlog()); // === "www.google.com"

: 위와 같이 새로운 객체의 속성들을 함수로 넣어주고 이것을 Person.prototype으로 설정하니 새롭게 생성된 Person의 각 객체들 unikys와 stranger에서는 이 함수를 사용할 수 있게 되었다. 위의 ECMAScript의 정의에서 공유할 수 있는 속성들을 가지고 있다는 것이 바로 이 뜻이다. 즉, C++이나 자바에서 흔히 사용하는 클래스의 함수 선언과 같이 생각하면 쉬울 것이지만, 이 prototype 또한 하나의 객체로 이루어져서 유연하다는 것이 조금 다른 점이다. 예를 들면, 위의 소스에 이어서 아래와 같이 더 덧붙일수도 있다.


Person.prototype.introduce = function () {
    alert("Hi!, my name is " + this.name + ", please visit my blog " + this.blog);
};
unikys.introduce();  // === Hi!, my name is unikys, please visit my blog unikys.tistory.com


: 나중에 prototype에 특정 속성을 추가하게 되면 나머지 다른 객체들에도 전부다 똑같이 공유되는 속성으로 추가되는 것이다. 참으로 유연하고 놀랍다. 비슷하게, 모든 객체들이 공유하는 변수 또한 추가할 수 있다. 그냥 함수 대신 변수를 넣어주면 공유하는 변수가 되는 것이다.


Person.prototype.gender = "male";
alert(unikys.gender); // === "male"
alert(stranger.gender); // === "male"


: 하지만 여기서 궁금증이 약간 생길 것이다. 만약 stranger의 gender를 바꾼다면 나머지 unikys에서의 gender도 바뀌는, static 처럼 동작하게 될까?


Person.prototype.gender = "male";
alert(stranger.gender); // === "male";
stranger.gender = "female";

alert(unikys.gender); // === male (not female?)
alert(Person.prototype.gender); // === male (also not female???)


: 위의 결과 stranger는 female로 바뀌었지만 unikys는 여전히 male을 출력하고 있는 것을 확인할 수 있고, unikys의 gender는 그렇다쳐도 직접 stranger.gender를 가져와서 처음에 male이라고 출력하는 것을 확인해봐서 stranger.gender를 설정했는데도 Person.prototyp의 gender는 그대로 값을 유지되고 있다. 이러한 현상이 왜 일어날까 한번 알아보자.



* prototype과 constructor

: 위의 이상한 동작을 알아보기 전에 먼저 내부 메모리 구조가 어떻게 되는지 먼저 알아보자. 이제 ECMAScript에서 정의하고 있는 prototype과 constructor의 링크를 다시 들어가서 한번 읽어보자. 어떠한 내용을 주목하냐면, 정의가 아니라 NOTE 부분을 다시 살펴보자.


http://www.ecma-international.org/ecma-262/5.1/#sec-4.3.4


: constructor의 note는 아래와 같다.


The value of a constructor’s “prototype” property is a prototype object that is used to implement inheritance and shared properties.


: 그리고 prototype의 note는 아래와 같다.


When a constructor creates an object, that object implicitly references the constructor’s “prototype” property for the purpose of resolving property references. The constructor’s “prototype” property can be referenced by the program expression constructor.prototype, and properties added to an object’s prototype are shared, through inheritance, by all objects sharing the prototype. Alternatively, a new object may be created with an explicitly specified prototype by using the Object.create built-in function.


: 이렇게 constructor.prototype이라는 속성이 바로 prototype을 접근할 수 있는 방법이라고 나와있는데, 그럼 이 constructor가 어디서 부여되는지 다시 ECMAScript 스펙을 찾아보자. 어느 부분을 찾아봐야하는지 곰곰히 생각해봐도 생각해내기 어려울테지만 Person이라는 선언을 한 것은 바로 function이었기 때문에 Function이 생성될 때 어떠한 일이 일어나는지 조금 더 자세하게 살펴보면 될 것이다.


http://www.ecma-international.org/ecma-262/5.1/#sec-13.2


: 위의 링크를 보면 Function 객체가 생성될 때 일어나는 일을 설명하고 있다. 따라서 function Person을 선언했을 때 일어나는 일들을 나열하고 있는데, 여기서 다른 부분은 생략하고 17번과 18번을 살펴보면 아래와 같다.


17. Call the [[DefineOwnProperty]] internal method of proto with arguments "constructor", Property Descriptor {[[Value]]: F, { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}, and false.

18. Call the [[DefineOwnProperty]] internal method of F with arguments "prototype", Property Descriptor {[[Value]]: proto, { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}, and false.


: 17번을 보면 proto에 "constructor"라는 속성으로 현재 생성자 함수 F를 부여하는 것을 볼 수 있고, 18번을 보면 F에 "prototype" 속성에 proto를 설정하는 것을 확인할 수 있다. F에 proto를 설정하고 proto에 F를 설정하니 바로 순환구조가 된다. 이것을 브라우져의 개발자 콘솔에서도 쉽게 확인할 수 있다. console.log(Person); 또는 console.log(unikys.constructor)를 해보면 그 안에 내용을 탐색할수가 있는데, 아래와 같은 구조를 확인할 수 있다.



: 이렇게 prototype 과 constructor의 순환 구조를 확인할 수 있다. 이것을 그림으로 나타내면 아래와 같이 할당되어있다는 것을 알 수 있을 것이다. 여기서 위에서 명시되어있는 F는 function Person이고, proto는 Person.prototype에 대입 시켰던 객체이며, F에서는 prototype이라는 속성으로 proto에 접근을 할 수 있으며, proto에서는 constructor라는 속성으로 F에 접근을 할 수 있는 순환 구조이다.



: 그렇다면 여기서 new Person()으로 객체를 생성하게 되면 어떠한 일이 일어나는지 살펴보면 아래 그림과 같이 된다. 여기서 주목할 것은 proto에서 정의된 getName과 getBlog 함수는 새로운 Person 객체에 들어가지 않고 계속 proto에 남아있다는 점이다. 그리고 새로운 Person 객체에서 F를 호출해서 초기화를 하게 되면 F 안에서 this.name과 this.blog를 설정함으로써 그 Person 객체에 name과 blog를 가지게 되는 것이다. 여기서 Implicit link라고 적은 것은 이렇게 Person 객체 안에서 this.getName 접근을 하면 참조는 할 수 있지만 직접 this.getName을 수정하려고 한다면 proto의 getName이 수정되는 것이 아니라 Person 객체에 있는 getName이 추가되거나 수정되는 것이다. 



: 그리고 재미있는 것은 그렇다면 Person 객체에서 F를 접근하려고 한다면 어떻게 해야할까 하는 질문이다. proto에 대한 implicit link가 있기 때문에 this.constructor 또는 위의 예에서 unikys.constructor로 접근하면 proto에 있는 .constructor를 바라보고 F를 접근할 수 있게 되는 것이고, 위의 순환 구조를 따라서 prototype 또한 접근을 할 수 있게 된다.


unikys = new Person();
alert(unikys.constructor === Person);  // === true
alert(unikys.constructor.prototype === Person.prototype);  // === true

: 이렇게 그리고 보면 이제 위의 gender를 설정하던 예에서 동작이 왜 그렇게 되었는지 알 수 있게 된다. 다시 소스를 가져와보면 아래와 같은데, 이 실행 결과는 아래 그림과 같다.


function Person() {};
var unikys = new Person(), stranger = new Person();
Person.prototype.gender = "male";
stranger.gender = "female";




: 위와 같이 stranger.gender를 설정하게 되면 proto의 gender가 바뀌는 것이 아니라 stranger에 gender를 생성하여 저장하게 되기 때문에 Person.prototype.gender는 그대로 유지되고, unikys.gender는 proto의 gender를 참조하기 때문에 male로 남아있게 된다. 따라서 만약 모든 Person 객체를 공통적으로 바꾸고 싶다면 proto에 있는 gender를 바꾸게 되면 모든 Person 객체에 공통되게 바뀌게 될 것이다.


function Person() {};
Person.prototype.gender = "male";
var unikys = new Person(), stranger = new Person();
alert(unikys.gender === "male" && stranger.gender === "male");  // === true;
Person.prototype.gender = "female";
alert(unikys.gender === "female" && stranger.gender === "female");  // === true;


 

* 속성 탐색 순서

: 위와 같은 구조 때문에 자기의 변수를 접근하고자 한다면 일단 자기 자신 객체의 속성부터 찾아서 존재하면 리턴하고, 없으면 자신의 proto에 저장되어있는 속성들을 검사해보고 있으면 리턴 없으면 undefined를 리턴하게 되는 것이다. 여기서 proto는 다른 객체가 될 수 있다는 점을 살펴보면 proto를 또 다른 F와 proto를 가진 객체로 설정하게 된다면 그것이 바로 상속의 기본적인 형태가 되는 것이고, 변수 탐색이 prototype을 따라서 쭉 연결되기 때문에 이것을 "prototype chain"이라고 불리운다. 간단한 상속을 그 유명한(?) Car 예제를 통해서 살펴보면 아래와 같다.


function Car() {
    this.wheel = 4;
    this.beep = "BEEP!";
};
Car.prototype.go = function () {
    alert(this.beep);
};
function Truck() {
    this.wheel = 6;
    this.beep = "HONK!";
};
Truck.prototype = new Car();
function SUV() {
    this.beep = "WANK!";
};
SUV.prototype = new Car();
var truck = new Truck(), suv = new SUV();
alert(truck.wheel === 6 && suv.wheel === 4);  // === true;
alert(truck.beep === "HONK!" && suv.beep === "WANK!");  // === true;
truck.go(); // === "HONK!"
suv.go(); // === "WANK!"


: 각 Truck 함수와 SUV 함수의 prototype을 new Car()로 새로운 객체를 생성하면 거기서 truck.go()로 go 속성을 참조하려고 할 때 다음과 같은 순서로 찾게 되는 것이다.


  1. truck에 go 속성이 있는지 검사
  2. Truck.prototype에 go 속성이 있는지 검사 = new Car()에 속성이 있는지 검사
  3. new Car()에 없으면, 해당 객체의 prototype인 Car.prototype에 go 속성이 있는지 검사
  4. Car.prototype.go 발견

: 이단계들은 객체의 속성을 접근할 때 재귀적으로 계속 해당 객체 -> prototype에 있는지를 검사하는 단계를 거치게 되는 것이다.



* hasOwnProperty의 활용

: 여기서 이제 다시 재조명시켜봐야할 함수가 하나 있다. 바로 모든 객체에 기본적으로 들어있는 hasOwnProperty 함수이다. 이 함수의 역할은 접근하려고하는 속성이 현재 객체에 포함된 것인지 아닌지를 구분하는 함수로, 이 함수를 이용하면 현재 객체의 속성인지 prototype안에 있거나 위와 같이 생성된 prototype chain 에 있는 것인지 구분할 수 있게 도와준다. 이렇게 활용할 예를 살펴보면 for-in 을 이용할 때가 대표적이다. 아래는 간단하게 특정 객체에 어떠한 속성과 값이 있는지 알아보고 싶을 때 for-in을 이용한 예이다.


function Person(name, blog) {
    this.name = name;
    this.blog = blog;
}
Person.prototype.getName = function () {
    return this.name;
};
Person.prototype.getBlog = function () {
    return this.blog;
};
var unikys = new Person("unikys", "unikys.tistory.com");
for (var prop in unikys) {
    console.log("unikys[" + prop + "] = " + unikys[prop]);
}


: 위와 같이 실행하면 아래와 같이 결과가 나온다.



: 이렇게 unikys의 name과 blog 속성 뿐만아니라 unikys의 prototype에 들어있는 getName과 getBlog까지도 출력되는 것을 볼 수 있다. 이러한 현상을 방지하기 위하여 hasOwnProperty를 사용하면 prototype에서 가져오는 속성들을 걸러낼 수 있게 된다.


for (var prop in unikys) {
    if (unikys.hasOwnProperty(prop)) {
        console.log("unikys[" + prop + "] = " + unikys[prop]);
    }
}

: 그럼 아래와 같이 결과가 출력된다.



: 따라서 이렇게 해당 객체의 속성만을 접근을 하고자할 때에는 hasOwnProperty 함수를 이용해서 접근을 하면 좋고, Douglas Crockford가 만들었던 자바스크립트의 코딩 스타일을 점검해주는 JSLint에서도 for-in을 사용할 때 hasOwnProperty로 속성을 점검해줄 것을 권장하고 있다. 지금 당장은 이러한 경우가 언제 필요할지 감이 잘 안올지도 모르지만, 다양한 객체를 활용하는 복잡한 자바스크립트를 개발하게 되면 습관을 들이면 좋을 코딩 습관이다.



* 왜 prototype을 사용해야하는가?

: 사실 굳이 복잡하게 공유하는 prototype에다가 함수를 설정하고 하지 않고 직관적으로 constructor에 직접 this의 속성으로 변수를 추가하듯 함수를 일일이 추가할수도 있다. 그렇다면 constructor를 사용해서 초기화하는 모든 객체들은 해당하는 함수를 가지게 될것이지만 왜 prototype을 사용하는 것이 더 좋을까? 이에 대해서는 예전에 '자바스크립트 라이브러리 만들기'글을 쓰면서 잠깐 다룬 적이 있는데 constructor로 생성하듯 연관배열로 함수를 저장하는 방법과 prototype으로 저장하는 방법을 조금 비교해봤었다.


2012/11/01 - [자바스크립트 라이브러리 만들기] 5. 기본 지식 - 연관 배열과 prototype (클래스 멤버 함수 선언하기)


: 이때에는 막연하게 여러 개의 객체들이 같은 메모리에 함수를 공유한다고 써놨지만 위의 prototype을 참조하고 있는 unikys와 stranger가 나와있는 그림에서 보듯이 prototype이라는 객체 하나가 함수/변수들을 가지고 여러 객체들과 서로 공유를 하는 구조를 가지게 되는 것을 명시적으로, 이론적으로 더욱더 명확하게 알 수 있게 되었을 것이다. 따라서, 만약 여러 객체들을 생성해야하는 상황이라면 이렇게 prototype을 사용할 것을 권장한다.



* prototype의 단점

: 하지만 언제나 만능은 없는 법. prototype의 단점들도 몇가지 꼽을 수가 있는데, 그 중 가장 눈에 띄는 것은 바로 자바스크립트에 익숙하지 않은 사람들은 도무지 봐도 이해를 할 수가 없다는 점이다. 위의 gender 예에서도 그랬지만, 일반 개발자로서는 얼핏봐서는 이해할 수 없는 동작들이 일어나기도 하고, prototype이라는 속성이 자동으로 생성된다는 점과 그 이름 자체 prototype에 거부감을 느끼면서 현기증이 나기도 한다. 하지만 이것은 자바스크립트 개발자로서 위의 내용들을 기본 지식으로 가지고 있으면 해결될 일이다. 위의 단점보다도 더 큰 고칠 수 없는 치명적인 단점이라면 퍼포먼스가 안 좋아질 수 있다는 점이다. 위의 prototype chain을 따라서 먼저 자기 자신이 속성을 가지고 있는지 체크하고 없으면 prototype, 또 없으면 prototype의 prototype 이런식으로 연쇄적으로 검사를 하는 것 자체가 다소 자원 소모적인 검색이기 때문에, 해당 객체에서 자주 사용할 속성이라면 prototype에는 기본값만 놔두고 자기 자신의 속성으로 새로 설정하여 최대한 prototype chain에 깊게 들어가서 검색하는 일을 최소화 시키는 것이 현명한 prototype의 활용법일 것이다.



* 정리

  • Function을 생성할 때 기본적으로 prototype 속성이 생성된다.
  • 이 prototype을 다른 객체로 설정함으로써 다른 객체의 속성들과 함수들을 공유할 수 있다.
  • 객체는 prototype에 implicit link로 연결되어있어 직접 this.constructor.prototype으로 접근하지 않으면 값이 수정되지 않고, 같은 속성명으로 값을 설정할 경우 prototype의 값은 사라진 것이 아니라 가려진 것일 뿐이다.
  • prototype은 객체를 생성함에 있어서 같은 메모리를 공유하므로 메모리 자원을 절약할 수 있다.
  • prototype은 상속의 가장 기본적인 방법으로 활용 가능하다.
  • 하지만 여러 단계의 prototype chain을 만들면 퍼포먼스가 안 좋아질 수 있으므로 긴 prototype chain은 자제하자.


* 다음에는 이번에는 prototype에 대하여 이론적으로 많이 다뤘으니 다음에는 조금 더 실용적인 예와 다른 여러 가지 방석의 객체 상속 방법들에 대하여 알아보자.


[속깊은 자바스크립트 강좌] 객체지향의 기본: prototype 끝.


- 다음 편

2013/10/04 - [속깊은 자바스크립트 강좌] 상속, new와 Object.create의 차이

2013/10/29 - [속깊은 자바스크립트 강좌] 글로벌(전역) 변수와 window 객체

2013/11/06 - [속깊은 자바스크립트 강좌] 변수 선언 방법에 대하여

2016/11/13 - [속깊은 자바스크립트 강좌] 마무리(는 책으로!)



저작자 표시 비영리 동일 조건 변경 허락
신고

이 글을 공유하세요.

  • 홍승우 2013.03.13 14:43 신고  댓글주소  수정/삭제  댓글쓰기

    좋은정보 감사합니다.^^

  • 이주영 2013.05.02 19:48 신고  댓글주소  수정/삭제  댓글쓰기

    자바를 공부하다, 자바스크립트를 접하게 되었는데.
    사실 햇갈린 부분이 없지않아 있네요.
    하지만 반복학습으로 이해하면 되는부분이고,
    작성자님 글을 매우 잘쓰시네요.
    비유력도 좋으시구 자주와서 공부해야 겠네요.
    강좌 너무너무 감사드립니다.^^

    • Unikys 2013.05.06 13:29 신고  댓글주소  수정/삭제

      댓글 감사합니다^^ 헷갈린 부분은 언제든지 물어보세요~
      후편을 작성해야하는데 요즘 일이 너무 바빠서 진행을 못하고 있네요 ㅠ 일이 좀 풀리면 다시 재개하도록 하겠습니다!

  • flower 2013.05.29 17:49 신고  댓글주소  수정/삭제  댓글쓰기

    자바스크립트로 개발하고 있는데요...
    생각없이 사용하던 부분들에 대해서 많이 배우고 갑니다.
    자바스크립트도 조금 깊게 들어가니 이렇게 어려울 거라고는.. ㅠㅠ
    혹시 다음편은 언제 나올까요? 다음편 기다리고 있네요...ㅎㅎ
    좋은 강좌 감사합니다..^^

    • Unikys 2013.10.29 09:55 신고  댓글주소  수정/삭제

      깊이들어가면 어렵지만 돌아가는것을 조금만 이애하면 정말 좋습니다. 자바스크립트로는 못할게 없다는 생각이 들지요^^

  • 절대자유 2013.06.30 13:06 신고  댓글주소  수정/삭제  댓글쓰기

    속깊은 강좌 시리즈 잘 봤습니다. 알듯말듯 했던 여러가지 개념들 이해하는데 많은 도움이 되어 정말 감사하다고 알씀드리고 싶네요.

    closure, this 개념을 더 확실하게 알게된 것 같습니다.

    개인적으로 강좌를 다시 이어갈 수 있기를 바라봅니다

    • Unikys 2013.10.29 09:54 신고  댓글주소  수정/삭제

      댓글 달아주신 덕분에 다시 조금씩 재개를 했습니다~ 지금까지는 좀 너무 깊이있고 비실용적인 내용이었는데, 이제는 좀 더 실용적인 내용을 다루려고 합니다!

  • k2five 2013.07.02 18:52 신고  댓글주소  수정/삭제  댓글쓰기

    설명 너무 잘 해주시는 것 같습니다. 감사드려요~

    • Unikys 2013.10.29 09:53 신고  댓글주소  수정/삭제

      제가 prototype 때문에 옛날에 고생한게 있어서 어떻게 이해해야지 쉽게 이해되는지 알고 있거든요 ㅋㅋ 자주 방문해주세요~

  • eehddl 2013.10.23 14:20 신고  댓글주소  수정/삭제  댓글쓰기

    잘 배우고 갑니다. 항상 prototype 이란게 궁금했는데 실제로 사용은 하지 못하고 있었거든요
    블로그 링크 걸어갑니다!

  • 흑 기사 2013.11.30 12:37 신고  댓글주소  수정/삭제  댓글쓰기

    요즘 제가 javascript에 대해서 궁금해서 리서치 중이었는데, 우연히 검색하다가 들르게 되었습니다. 덕분에 많은 도움이 되었습니다~ 좋은 글이라 제 블로그에 담고 싶네요. 원문출처는 반드시 표기하겠습니다. 다시 한번 좋은 글 고맙습니다~

  • 제임스안 2014.02.19 10:01 신고  댓글주소  수정/삭제  댓글쓰기

    prototype 관련해서 서치하다가 여기까지 오게 되었습니다. 정말 자세하게 잘 나와 있어서 많은 도움이 되었습니다. ㅎ 제블로그에 담아갈게요. 물론 출처는 표기 합니다. ㅎ
    그럼 수고하세요.

  • test 2014.07.08 15:27 신고  댓글주소  수정/삭제  댓글쓰기

    감사합니다.. 즐겨찾기 해놓았어요
    저도 요즘 ECMAScript Language Specification 보고 공부중인데 많은 도움 됬습니다.

    • Unikys 2014.07.15 15:07 신고  댓글주소  수정/삭제

      자바스크립트가 동작하면서 ?? 했던 것들도 정의를 보면 나와있죠. 새로운 부분들 알게 되시면 댓글 부탁드립니다.

  • betterLife 2014.07.17 16:28 신고  댓글주소  수정/삭제  댓글쓰기

    정말 좋은 글입니다. 이제야 prototype에 대한 감이 왔어요.
    감사합니다^^

  • nightrain 2015.07.04 21:26 신고  댓글주소  수정/삭제  댓글쓰기

    감탄하고 갑니다.
    앞으로 자주 들릴께요^^

  • 이윤구 2016.08.26 12:21 신고  댓글주소  수정/삭제  댓글쓰기

    아 정말 잘 읽고 갑니다.
    출판에 대한 얘기가 전에 있었던 것 같은데 아직 소식은 없나요?

  • 김나리 2017.03.23 09:52 신고  댓글주소  수정/삭제  댓글쓰기

    예제 코드에서
    Person.prototype = { getName: function () { return this.name; }, getBlog: function () { return this.blog; } };
    이렇게 되어있는데, 이렇게 하면 prototype 에 construct 가 안생기고
    Person.prototype.getName = function(){
        return this.name;
    }
    이렇게 해야 prototype 에 constructor 가 생기는데
    왜그런걸까요?

    • Unikys 2017.03.23 10:23 신고  댓글주소  수정/삭제

      네, 일단 construct가 아니라 constructor인지 확인 부탁드리고요. Person.prototype에 constructor는 생길건데요 이 두 가지는 내부적으로 조금 다른 부분이 있습니다. Person.prototype = {}; 등과 같이 새로운 객체를 prototype에 설정하게 되는거고, Person.prototype.getName = function(){} 등과 같이 하면 기존의 객체에다가 속성을 추가하는 거기는 한데 동일하게 prototype은 객체이기 때문에 둘다 Person.prototype.constructor == Object() 가 됩니다. 그리고 혹시 var unikys = new Person() 의 변수 unikys의 prototype을 찾으려고 하신거라면 prototype은 함수형만 가지고 있는 속성입니다. 변수에서 prototype을 확인하고 싶으시면 unikys.constructor.prototype 이렇게 접근해야 됩니다. 아마 다음편을 읽어보시면 조금 더 이해가 쉽게 되시리라 생각합니다.

질문이나 의견을 댓글로 달아 주세요