- new 연산자와 함께 사용되도록 설계된 함수를 생성자라고 한다.
- 생성자는 새로운 객체를 초기화 하고 객체에 있는 프로퍼티중에서 사용되기전에
미리 값이 할당되어야 하는 프로퍼티들의 초기값을 할당 한다.
- 생성자는 객체들의 클래스를 정의한다.
- 생성자는 일반적으로 리턴 값이 없이 this 키워드가 가리키는 객체들을 초기화
시키는 역할을 한다.
- 생성자 함수에서 결과값으로 객체를 리턴하면, 리턴값을 가질 수 있으며, new 문장의
결과 값이 된다. 이때, this가 가리키던 객체는 폐기 된다.
- 아래 처럼 적절한 생성자 함수를 정의하는 방법으로 객체들의 클래스를 만들수 있다.
function Rectangle( w , h ) {
this.width = w ;
this.height = h ;
}
var rect1 = new Rectangle( 2 , 4 ) ;
var rect2 = new Rectangle( 5 , 3 ) ;
// rect1 = { width : 2 , height : 4 } ;
* 자바스크립트 메서드
- 메서드는 객체의 프로퍼티 이자, 호출가능한 함수다.
- 메서드 내의 this 키워드는 메서드가 속한 객체를 가리킨다.
## Rectangle 객체로 표현된 사각형의 넓이를 구하는 몇가지 방식
첫번째 비객체지향적인 방식
function computeArea( rect1 ) {
return rect1.width * rect1.height ;
}
두번째 메서드 사용 방식
rect1.area = function () {
return this.width * this.height ;
}
var a = r.area() ;
세번째 메서드 사용방식 개량
function Rectangle( w , h ) {
this.whidth = w ;
this.height = h ;
this.area = function () {
return this.width * this.height ;
}
}
var rect1 = new Rectangle( 2 , 4 );
var a = rect1.area();
※ 생성자 내부에서 메서드를 추가하는 방법은 실행하는데는 문제 없지만
비효율적인 방식입니다. (메모리 활용 면에서..)
왜냐면..위의 생성자로 생성된 인스탄스들은 모두 프로퍼티를 세개를 같습니다.
width,height,area ==> 3개
모든 인스턴스가 공유해야하는 프로퍼티에 일반적인 프로퍼티를 사용하는것은
비효율적입니다. 차라리 공통의 프로토타입이라는 공통의 객체의 프로퍼티를 상속받아서
사용하는것이 메모리사용에 좀더 효율적입니다.
* 자바스크립트 프로토타입 (prototype)
- 모든 객체는 프로토 타입이라는 또다른 객체를 내부적으로 참조할 수 있다.
- 모든 객체는 프로토타입의 프로퍼티들을 자신의 프로퍼티로 가져온다
즉, 자바스크립트 객체는 자신의 프로토타입객체에 있는 프로퍼티를 상속 받는다.
- 모든 함수에는 prototype 이라는 프로퍼티가 있는데 함수가 정의되는 순간부터 자동으로
생성되고 초기화 된다.
- prototype 프로퍼티의 초기값은 프로퍼티가 하나 있는 객체로서 설정된다.
= 생성자 함수를 가리키는 contrtuctor 프로퍼티를 가지는 객체
- 프로토타입 객체에 추가한 프로퍼티들은 생성자를 사용하여 생성된 모든 객체의 프로퍼티로
나타난다.
Rectangle.prototype.area =
function () {
return this.width * this.height ;
} ;
※ 중요 !!
- 이 생성자 ,(Rectangle)를 사용해 생성되는 객체들은 모두 프로토타입 객체의
프로퍼티들을 상속받는다.
- 프로토타입객체에 새로운 프로퍼티가 추가되면, 이미 생성된 객체일지라도
새로 추가된 프로퍼티를 상속받는다.
- 기존의 클래스에 새로운 메서드를 추가할 수 있다는 의미가 됨
- 각 객체가 프로토타입 객체를 상속받는다.
각 객체로 프로토타입의 프로퍼티들이 복사되는 것이 아님!! (메모리 절약 !!)
Object.hasOwnProperty() 를 통해서 상속받은 프로퍼티인지 확인하수 있다.
var r = new Rectangle( 2 , 3 );
r.hasOwnProperty("width") ; // true width 는 r의 직접적인 프로퍼티 이다.
r.hasOwnProperty("area") ; // false area 는 상속받는 프로퍼티 이다.
"area" in r // true area 는 r 의 프로퍼티이다.
* 상속 받은 프로퍼티의 읽기와 쓰기
- 클래스에는 프로퍼티의 집합과 더불어 한 개의 프로토타입 객체가 있다.
- 클래스의 인스턴스는 여러개가 있을 수 있는데 ,각 인스턴스들이 각각
프로토타입의 프로퍼티들을 상속 받는다.
※ 한개의 프로토타입 객체의 프로퍼티를 여러개의 인스턴스들이 상속받기 때문에
프로퍼티 값을 읽거나 쓸때 비대칭성을 의무적으로 지키도록 하고 있다.
(읽고 쓸때 비대칭적이라는 의미임)
ex)
객체 obj 의 프로퍼티인 p를 읽을때 obj에 p라는 프로퍼티가 있는지 확인한다.
만약 없다면 obj 의 프로토타입 객체에 p라는 프로퍼티가 있는지 검사한다.
프로퍼티 값을 쓸때는 프로토타입 객체를 사용하지 않는다
객체 obj가 프로토타입에서 상속받은 프로퍼티인 p를 설정하려 하면,
obj 에는 새로운 프로퍼티 p 가 만들어 진다.
※ 상속은 읽을때만 일어난다 !!
자바스크립트는 p의 값을 찾으려 할때 먼저 obj 에서 있는지 확인한 후
없을 경우에만 프로토타입 객체에서 검사한다.
[ obj 에있는 프로퍼티 p 가 프로토타입 객체에 있는 프로퍼티 p를 가렸다 ]
* 자바스크립트 내장클래스의 슈퍼클래스
- 자바스크립트는 클래스 기반의 언어구조적인 상속대신 프로토타입의 상속을 지원한다.
※ 자바에서는 언어적으로 extends 라고 선언해주면 되지요...
- Object 클래스는 모든 내장 클래스들의 슈퍼클래스이고, 따라서 모든 클래스는
Object 클래스의 몇개의 기본적인 프로퍼티,메서드를 상속 받는다.
- 객체는 생성자 함수의 프로토타입 객체에 있는 프로퍼티들을 상속받는다.
- 프로토타입 객체 역시 하나의 객체이므로 Object() 생성자를 사용하여 만들어진다.
--> 프로토타입 객체도 Object,prototype의 프로퍼티를 상속받는다.
- 프로토타입의 상속은 한개의 프로토타입에 국한되지 않는다. 대신 프로토타입 객체의
체인을 사용한다. 즉, Complex 객체는 Complex.prototype과 Object.prototype 의 프로퍼티를
상속받는다.
* 자바스크립트 일반클래스의 서브클래스 정의하기
ex)
//슈퍼클래스 정의
//이 클래스의 서브클래스를 정의한다.
function Rectangle(w,h){
this.width = w ;
this.height = h ;
}
//슈퍼클래스의 프로토타입 객체에 area 프로퍼티 정의 및 초기화
Rectangle.prototype.area = function() {
return this.width * this.height ;
}
//서브클래스의 생성자에서 슈퍼클래스의 생성자를 호출한다.
//이 때, 슈퍼클래스의 생성자가 새로만든 객체의 메서드인것처럼 호출한다.
//생성자 체이닝
function subRectangle ( x , y , w , h ){
Rectangle.call ( this , w , h ) ;
//= subRectangle.Rectangle(w,h);
//= subRectangle.width = w ;
//= subRectangle.height = h ;
//this 객체는 Rectangle 생성자함수가 소속되어 호출될 객체이다. (subRectangle 생성자)
//Rectangle 생성자 함수를 this 객체의 메서드인것처럼 호출
※ call () 첫번째 전달인자 : 함수가 소속되어 호출될 객체 지정
함수의 몸체 안에서 this 키워드의 값이 된다.
나머지 전달인자 : 함수가 호출될때의 전달인자.
this.x = x ;
this.y = y ;
}
//서브클래스의 프로토타입 객체를 명시적으로 슈퍼클래스의 인스턴스로 설정한다.
//기본 프로토 타입 객체를 사용하면 Objec 클래스의 서브클래스가 된다.
//프로토타입 객체 역시 하나의 객체이므로 Object() 생성자를 사용하여 만들어지기 때문에.
//new 연산자는 빈 객체를 생성한 후 생성된 객체의 프로토타입 객체를 설정한다.
//이 때 생성된 객체는 자신을 만들어낸 생성자의 prototype 프로퍼티 값을
//자신의 프로토타입객체로 설정한다.
//subRectangle클래스의 프로토타입객체가 Rectangle 클래스의 인스탄스 이므로
//subRectangle은 프로토타입 객체인 Rectangle 인스탄스의 프로퍼티를 상속받게 된다.
subRectangle.prototype = new Rectangle();
//subRectangle.prototype 객체가 Rectangle 클래스의 인스턴스 이므로
//subRectangle 클래스의 모든 인스턴스는 프로토타입 객체인 Rectangle 인스탄스의
//모든 프로퍼티를 상속받게 될것이므로, 필요없는 프로퍼티는 삭제하여 준다.
delete.subRectangle.prototype.width ;
delete.subRectangle.prototype.height ;
//subRectangle 클래스의 프로토타입 객체가 Rectangle() 생성자를 사용해서 만들어졌으므로
//constructor 프로퍼티는 Rectangle()생성자를 참조한다.
//constructor 프로퍼티를 수정하여 준다.
subRectangle.prototype.constructor = subRectangle ;
이제 subRectangle 클래스의 프로토타입 객체가 만들어 졌으며,
여기에 새로운 메서드를 추가할 수 있다.
//호출할때~~
var r = new subRectangle(2,2,2,2) ;
참고로, new 가 빈객체를 생성한 후 생성자 함수의 프로토타입 객체를 설정하는데
여기서 생성자 함수의 프로토타입객체가 Rectangle 이죠~~~
그래서 Rectangle 이 프로토타입 기반의 수퍼클래스가 된다능~~~
* 생성자 체이닝
서브클래스의 생성자 함수 내에서 상위클래스의 생성자를 명시적으로 호출하는 것을
생성자 체이닝 이라고 한다.
call() 함수를 써서 생성된 객체의 메서드인것 처럼 호출했었는데 다음처럼 간단하게 고칠 수 있다.
//상위클래스에 대한 참고 저장
subRectangle.prototype.superclass = Rectangle ;
function subRectangle( x , y , w ,h ){
// 프로토타입 객체의 프로퍼티인 superclass 에 Rectangle 참조를 저장하여
// subRectangle 클래스의 메서드화 하여 실행함.
this.superclass (w , h) ;
this.x = x ;
this.y = y ;
}