본문 바로가기

Development/Javascript

[펌] 자바스크립트 정리. 5 - 자바스크립트 클래스,생성자,프로토타입

검색엔진을 뒤지면서 보다가 너무나 깔끔하게 정리된

자바스크립트가 있기에 퍼옴. GENERAL 님의 정리 감사합니다.


링크 : http://blog.naver.com/minis24/80094251902


* 자바스크립트는 자바와 같은 실제 클래스를 지원하지는 않는다.

    (자바스크립트 2.0에서는 지원한다고 합니다.

* 대신 자바스크립트는 프로토타입객체와 생성자 함수를 사용하여 모조클래스를 구현할 수 있다.

   (이하 클래스라 함)

 

 

* 자바스크립트 생성자

     - 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 ;

   }