9장 클래스, 생성자, 프로토타입
많은 OOP 언어들이 객체의 클래스를 정의하고 정의된 클래스의 인스턴스를 생성하는 기능을 제공하지만 xxxxxxjavascript는 실제 클래스(true class)를 지원하지 않는다. 대신 모조 클래스(pseudoclass)를 정의할수 있다.
모조클래스는 프로토타입 객체나 생성자 함수를 사용하여 구현할수 있다.
9.1 생성자
new 연산자를 통해서 객체를 생성 new 연산자 뒤에는 반드시 생성자 함수를 호출해야함.
ex) function Rectangle(w, h){
this.width = w;
this.height = h;
//주의 여기에는 return 문이 없다.
}
//객채생성
var rect1 = new Rectangle(2,4); // rect1 = {width:2, height:4};
var rect2 = new Rectangle(3,5); // rect1 = {width:3, height:5};
이렇게 생성자만 정의하는 방법으로 객체들의 클래스를 만든다.
이런방식으로 OOP 코드를 작성을 한다.
new가 return value 를 가지고 있다면 변수에 할당되는 것은 인스턴스가 아니고 return value임(test해봤는데 이상함)
그리고 this 가 가리키고 있던 값은 폐기된다고 한다.
9.2 프로토타입과 상속
자바스크립트의 모든 객체는 prototype이라고 불리는 객체를 내부적으로 참조할수 있다.
그렇게 해서 prototyope의 모든 프로퍼티들을 자신의 프로퍼티처럼 참조할 수 있다.
다시 말해 prototype의 모든 property를 상속받는다고 말할 수 있다.
그렇다면 프로토타입은 언제 쓰느냐?(생성자 호출시 메소드를 정의할경우와 그렇지 않을 경우?);
프로토타입으로 추가하는 모든 속성은 객체가 생성된 후에 추가가 되더라도 참조할수 있음.
예를들어 면적을 구하는 메소드가 존재할 경우는 모든 rectangle 마다 같으므로 내부에서 선언해서 메모리를 낭비할 필요가 없다.
이럴때
Rectangle.prototype.area = function(){return this.width * this.height}
를 추가하면 모든 Rectangle 의 clone들은 Rectangle.area()를 호출할수 있다.
new를 통해서 생성되는 객체는 생성자의 prototype을 자신의 prototype으로 설정한다.
모든 함수는 prototype을 가지고 함수가 정의될때 자동으로 생성되고 초기화된다.
이 때 하나의 초기값을 가지고 있는데 이 property는 constructor라고 불리며 프로토타입과 연관되어있는 생성자 함수를 가리킨다.
프로토타입 체인에 의해서 참조하고 있는 프로퍼티(책에서는 '상속받은 프로퍼티'라고 표현하고 있음 )들은 객체의 일반적인 프로퍼티처럼 동작한다. 따라서
for in roop 에 의해 열거될수가 있다. 이를 구별하기 위해서 Object.hasOwnProperty()를 사용해야할 필요가 있을 때도 있다.
9.2.1. 상속받은 프로퍼티 읽기와 쓰기
object.prototype.property
object.property 차이점
js는 어떤 property 를 참조할 때 object.property 가 있는지 검사한 후 object.prototype.property 를 검사한다.
이것이 프로토타입 체인을 통해서 oop를 가능하게 하는 원리이다.
9.2.2 내장형 타입의 확장자.
String.prototype.trim();
다른 개발자에게 혼란을 줄 수 있음.
object.prototype은 건들지 말자~~!!!
core를 건들때 좋은점과 안 좋은 점.
-객체를 연관배열로 사용하는 코드가 작동하지 않을수도 있다.
-논외긴 하지만 커멘트
prototype은 건들고 있음.
jQuery 는 안 건들고 있음..
둘의 차이점. -> iframe내에서 사용할 때?
9.3 자바스크립트의 클래스 시뮬레이션
9.3.1 인스턴스 프로퍼티
모든 객체에는 자신만의 인스턴스 프로퍼티 사본이 있다.
다시 말해 한 클래스에 속하는 객체가 열개가 있다면 인스턴스 프로퍼티 사본이 열개 만들어진다.
예를들어 Rectangle 클래스의 모든 객체는 사각형의 너비를 나타내는 width 프로퍼티가 있는데 이것이 바로 인스턴스 프로퍼티이다.
9.3.2 인트턴스 메소드
인스턴스 프로퍼티와 유사함
차이점은?
9.3.2.1 인스턴스 메소드에서 this의 scope
C++의 인트선스 메소드의 유효범위는 this객체를 포함한다.
Rectangle을 예로 들면
return width * height 가능..
하지만 xxxxxxjavascript에서는
return this.width * this.height
이게 어색하면
Rectangle.prototype.area = function(){ width(this){return width* height;}}
9.3.3 클래스 프로퍼티
클래스 자체와 연관되어있는 프로퍼티. 인스턴스의 갯수와 상관없이 1개만 존재. 클래스 프로ㅓ트는 클래스 자신을 통해서 접근.
유용한점: 클래스와 연관되어있고 논리적인 보호 영역이 있다는 점. 같은이름을 가진 다른 프로퍼티에 의해서 오버라이팅 되지 않는다.
Rectangle.UNIT이라는 클래스 프로퍼티를 만들고 싶다면...아래처럼.
Rectangle.UNIT =newRectangle(1,1); // 1x1 사각형을 저장.
Rectangle은 생성자 함수이지만 js함수는 객체이기 때문에 다른 객체에 프로퍼티를 만드는 것과 똑
같이 함수프로퍼티를 만들수 있다.
9.3.4 클래스 메소드
static class의 메소드를 사용하는것처럼 쓰면 된다.
9.3.7 Private 멤버
xxxxxxjavascript는 closure를 통해서 정보의 은닉이 가능.
function ImmutableRectangle(w, h){
this.getWidth = function(){return w;}
this.getHeight = function(){return h;}
}
ImmutableRectangle.protototype.area = function(){
return this.getWidth() * this.getHeight();
}
9.5 슈퍼클래스와 서브클래스
xxxxxxjavascript에도 클래스 계층을 유사하게 만들수 있음.
자바스크립트에서는 Object 클래스가 가장 일반화 되어 있는 클래스이며
다른 클래스들은 이 클래스의 서브 클래스이거나 이를 좀 더 구체화시킨 클래스임
프로토타입 = 하나의 객체임
프로토타입 객체도 Object() 생성자를 통해서 생성됨
이말은 프로토타입객체도 Object.prototype의 프로퍼티를 상속받는 다는 말.
예를들어서
Rectangle class를 상속받는 sub class를 생성하려면
1. PositionedRectangle(x,y,w h){}
Rectangle.call(this,w,h)를 호출한다. 생성자 체인이라고 함.
Rectangle을 서브클래스화 시키려면 명시적으로 프로토타입 객체를 생성해야한다.
PositionedRectangle.prototype = new Rectangle();
9.6 상속없이 확장하기
그냥 property 를 copy 하는 것
for(minform ){ to[m] = from[m] }
9.7 객체타입 판단하기
instanceof 연산자와 constructor 프로퍼티의 한가지 단점은 이미 알고 있는 클래스만 테?할수 있다는 점이다.
이 기능은 디버깅할때 모르는 객체를 조사하는데는 유용하지 않다. 이 경우에는 Object.toString()의 기본 구현을 사용하는 기법을 유용하게 활용할 수 있다.
function getType(x){
if(x == null) return 'null';
var t = typeof x;
if(t != "object") return t;
var c = Object.prototype.toString.apply(x);
c = c.substring(8, c.length-1);
if( c != "Object") return c;
if(x.constructor == Object) return c;
if("classname" in x.constructor.prototype && typeof x.constructor.prototype.classname =="string") return x.constructor.prototype.classname;
return "<unknow type>";
}