Javascript의 프로토타입
프로토타입(Protytype) 이란?
자바스크립트의 모든 객체는 프로토타입이라고 불리는 또 다른 객체를 내부적으로 참조할 수 있다. 그리고 객체는 프로토타입의 프로퍼티들은 자신의 프로퍼티로 가져온다.
자바스크립트는 자바, C++, C#에서 제공하는 것과 같은 실제 클래스(true class)를 지원하지 않는 대신 모조 클래스(pseudoclass)를 정의할 수 있다. 결국 Prototype은 Class 개념이 없는 Javascript에서 객체를 확장하고, 객체 지향적인 프로그래밍을 할 수 있게 도와주는 일종의 모조 class 개념이다.
1 | 모든 Javascript 객체는 prototype을 가지고 있다. 또한 그 prototype은 object이다. 모든 자바스크립트 Object는 그들의 프로토타입으로부터 properties와 methods를 상속받는다. |
객체 지향 프로그래밍
객체지향 언어(Object-oriented) 언어는 일반적으로 클래스를 통해 프로퍼티와 메서드를 가지는 객체를 여러개 만든다는 특징이 있다. 그러나, javascript는 클래스 개념이 없으며, 다른 클래스 기반 언어와는 다르다. ECMA script 에서는 객체를 프로퍼티의 순서 없는 컬렉션이며, 각 프로퍼티는 원시값이나 객체, 함수를 포함한다
(특별한 순서가 없는 값의 배열) 라고 명시한다. 최종적으로 객체 프로퍼티의 모든 값은 원시값을 가진다.
1 | var person = { |
- person이라는 객체는 name, age, gender 라는
프로퍼티
로 이뤄져 있다. - 프로퍼티에는 데이터 프로퍼티와 접근자 프로퍼티 두 가지 타입이 있니다.
데이터 프로퍼티
- 데이터 값에 대한 단 하나의 위치를 포함하여 이 위치에서 값을 읽고 쓸수 있다.
4가지 속성은 아래와 같다
- Configurable - 해당 프로퍼티가 delete를 통한 삭제, 속성 변경, 접근자 프로퍼티로 변환할 수 있음을 나타냄 (default: true) => 실험결과 false
- Enumerable - for-in 루프에서 해당 프로퍼티를 반환할 수 있음을 나타냄 (default: true)
- Writable - 프로퍼티 값을 바꿀 수 있음을 나타냄 (default: true)
- Value - 프로퍼티의 실제 데이터를 포함, 프로퍼티의 값을 읽는 위치이며 새로운 값을 쓰는 위치 (default: undefined)
객체에 프로퍼티를 명시적으로 추가할 때 Configurable, Enumerable, Writable의 속성은 true, Value에는 할당된 값이 지정된다.
- 위의 예제에서 name이라는 프로퍼티의 Value는 ‘김철수’로 저장된다.
1 | //Writable |
접근자 프로퍼티
getter와 setter 함수로 구성
- Configurable - 해당 프로퍼티가 delete를 통한 삭제, 속성 변경, 접근자 프로퍼티로 변환할 수 있음을 나타냄 (default: true) => 실험결과 false
- Enumerable - for-in 루프에서 해당 프로퍼티를 반환할 수 있음을 나타냄 (default: true)
- get - 프로퍼티를 읽을 때 호출하는 함수
- set - 프로퍼티를 바꿀 때 사용하는 함수
getter 함수는
읽으려 할 때
발생하며, setter 함수는바꾸려는 시도가 있을 경우
실행- getter 함수만 지정하는 경우, 해당 프로퍼티는 읽기 전용이 되며 바꾸려는 시도는 무시
1 | var person={ |
객체 생성하기
- 객체는 복합 타입(composite datatype)
여러 값들의 결합체로, 각자의 이름을 통해 원하는 값 저정/읽기가 가능
- 객체 리터럴 - 객체를 생성하는 가장 쉬운 방법이다.
1 | var person = { |
- 객체 리터럴로 생성자로 생성하게 될시, 같은 인터페이스를 가진 객체를 여러개 만들 때는
중복된 코드를 많이 써야 한다는 단점
이 존재 => `팩터리 패턴
팩터리 패턴
- 팩터리 패턴이란 특정 객체를 생성하는 과정을 추성화 한 것으로 소프트웨어 공학에서는 잘 알려진 디자인 패턴으로 Spring 과 같은 프레임워크에서 많이 사용한다.
1 | function createObject(name, age, gender){ |
- 단점으로는 생성한 객체가 어떤 타입인지 알 수 없다는 문제가 있다.
생성자 패턴
- 생성자 패턴이란 특정한 타입의 객체를 만드는 데 사용한다.
- 명시적으로 객체를 생성하지 않음.
- 프로퍼티와 메서드는 this 객체에 직접 할당
- return 문이 없음.
1 | function Person(name, age, gender){ |
하지만 이러한 생성자 패턴 역시 인스턴스마다 메서드를 생성하게 되어 성능적인 부분에서 좋지 않다. 예를 들어 아래의 예제를 보면 조금 더 이해하기가 쉽다.
1 | function Person(name, age, gender){ |
프로토 타입
모든 함수는 prototype 프로퍼티를 가지며, 이 프로퍼티는 해당 참조 타입의 인스턴스 가져야 할 프로퍼티와 메서드를 담고 있는 객체
이다. prototype의 property와 method는 객체 인스턴스 전체에서 공유되어 prototype의 property를 상속받은 객체들의 상당한 메모리를 줄일 수 있다. 또한 prototype에 새로운 property가 새로 추가되면 이미 생성되었던 객체일지라도 추가된 property를 그대로 상속받는다. 이 점은 상황에 따라 장점일 수도, 단점일 수 도 있다.
1 | function Person(){}; |
위의 생성자 패턴과는 다르게 ‘getThis’ 라는 함수가 같다고 나온다. 이는 프로퍼티와 메서드를 모든 인스턴스에서 공유한다는 것을 의미한다.
p.267
1 | var a = new Date(); |
예를 들어서, 위와 같이 a 객체를 Date() 라는 생성자 함수를 이용하여 만들 경우, new 연산자는 객체a 의 프로토 타입을 설정하게 되는데, 객체 a 의 프로토 타입은 자신을 만들어낸 생성자 함수 Date() 의 프로토타입 프로퍼티값을 자신의 프로토타입으로 설정한다.
(모든 함수에는 prototype이라는 프로퍼티가 있는데, 이 것은 함수가 정의될 때부터 자동적으로 생성되고 초기화된다.)
1 | function func01(){} |
프로퍼티의 상속에 대해 조금 더 살펴보면, 위의 소스를 기준으로 “test.temp” 를 console로 찍어보면 “true” 값이 나온다. 그 이유를 분석해보면,
- func01의 프로토타입에 temp라는 프로퍼티의 값을 true로 설정하였다.( 1-2번째줄 )
- func02의 프로토타입은 생성자 함수 func01로 만들어 func01의 프로퍼티를 그대로 상속한다. (4-5번째줄)
- func03 역시 프로토타입을 생성자 함수 func02로 만들어 func02의 프로퍼티를 그대로 상속한다. (7-8번째줄)
- 변수 test는 생성자 함수 func03으로 만들어 func03의 프로퍼티를 그대로 상속한다.(10-11번째줄)
결국 test는 생성자 func03의 프로퍼티를, func03은 func02의 프로퍼티를, func02는 func01의 프로퍼티를 상속받는다. 그래서 console에 test.temp를 찍으면 true 값이 나오게 된다.
프로토 타입 사용법
1 | function func(){ |
위의 소스를 보면 func() 생성자를 사용하여 만드는 객체 test1_1에는 test1과 test2라는 프로퍼티가 있다. 이 말은 func() 생성자를 사용하는 모든 객체에는 2개의 프로퍼티가 존재한다는 것이다.