ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 자바스크립트 - 객체지향 프로그래밍 - Class 만들기
    Dev/Javascript 2019. 10. 25. 23:37

    자바스크립트는 프로토타입 기반 객체지향 언어이기 때문에 Java와 같은 언어처럼 클래스가 없습니다.

    그러나 프로토타입 체인과 생성자함수를 이용하면 클래스와 같은 기능을 하는 함수를 만들 수 있습니다.

     

    이번엔 자바스크립트의 특성을 이용해 클래스를 만드는 makeClass 함수를 만들어보도록 하겠습니다.

     

    - makeClass는 상속받을 객체를 인자로 받아 부모 함수를 상속받는 자식 클래스를 만듭니다.

    만들고자 하는 함수의 활용은 다음과 같습니다.

    var SuperClass = makeClass(obj);
    var SubClass = SuperClass.makeClass(obj);

     

    함수 makeClass는 다음과같이 구성됩니다.

    function makeClass(obj) {
      /*
        1. 자식클래스 생성
        2. 생성자 호출
        3. 프로토타입 체인을 활용한 상속 구현
        4. obj를 통해 들어온 변수 및 메서드를 자식 클래스에 추가
        5. 자식함수 객체 반환
      */
    }
    

    기능의 목록을 보면 자바의 클래스와 매우 비슷하죠?

    만들어가는 과정 보다, 완성된 코드를 보고 코드설명을 하는게 더 이해가 빠를 것 같아 

    먼저 완성된 코드를 보겠습니다.

    function makeClass(obj) {
      var parent = this === window ? Function : this; 
      var F = function() {};
    
      var child = function() {
        var _parent = child.parent;
    
        if(_parent && _parent !== Function) { 
          _parent.apply(this, arguments); 
        }
    
        if(child.prototype._init) {
          child.prototype._init.apply(this, arguments); 
        }
      };
    
      F.prototype = parent.prototype;   
      child.prototype = new F();        
      child.prototype.constructor = child; 
      child.parent = parent;              
      child.makeClass = arguments.callee;
    
      for(var i in obj) {
        if(obj.hasOwnProperty(i)) {
          child.prototype[i] = obj[i];
        }
      }
    
      return child; 
    }

    대략적으로 한번 보시면.

    - 프로토타입

    - 생성자함수

    두가지 개념만으로 클래스를 구현한 것을 알 수 있습니다.

     

    이제 위에서부터 구체적인 코드 본체를 살펴볼게요.

    var parent = this === window ? Function : this;

    -> 이중 ~ n중 상속이 될 수 있으므로 부모객체가 가장 상위 객체인지 아닌지 구별해줍니다.

    부모객체가 window(가장 상위 부모객체)와 같다면? parent를 Function으로 할당, 

    부모객체가 가장 상위 부모객체가 아니라면? this(부모객체)로 할당

     

    var F = function() {};

    -> 프로토타입 체인을 위해 빈 생성자 함수 생성

     

    var child = function() {          // 1)
      var _parent = child.parent;          // 2)
      
      if(_parent && _parent !== Function) {          // 3)
        _parent.apply(this, arguments);
      }
    
      if(child.prototype._init) {          // 4)
        child.prototype._init.apply(this, arguments);
      }
    }

    ->

    1)  리턴할 자식클래스 선언

    2) 자식의 부모를 _parent 임시 변수에 할당

    3) 부모가 최상위 부모가 아니라면 부모 함수 재귀적으로 호출(위에서 최상위 부모는 Function으로 초기화를 했죠.)

    4) 자식의 프로토타입 프로퍼티에 _init가 존재하면 _init 호출(생성자 함수 호출)

     

    F.prototype = parent.prototype;

    -> 프로토타입 체이닝을 위해 만든 임시 함수객체 F

     

    child.prototype = new F();

    -> 부모객체를 상속받게 하기 위해 프로토타입이 parent인 F 인스턴스를

    자식의 프로토타입에게 할당.

    이렇게 하면 자식 인스턴스는 프로토타입 체인으로 parent까지 참조 가능하게 됨. (부모에게 상속이 됨)

     

    child.prototype.constructor = child;          // 1)
    child.parent = parent;          // 2)
    child.makeClass = arguments.callee;          // 3)

    -> 

    1) 자식 프로토타입의 생성자를 정의 (constructor라는 프로퍼티에 자기 자신을 참조)

    2) 자식의 parent 프로퍼티에 부모 객체 참조

    3) 자식의 makeClass에 현재 호출된 함수인 makeClass 컨텍스트 참조

    for(var i in obj) {
      if(obj.hasOwnProperty(i)) {
          child.prototype[i] = obj[i];
      }
    }

    -> 상속받은 객체의 프로퍼티들을 자식의 프로토타입에 복사해준다.

     

    return child;

    -> 부모에게 상속이 끝난 자식클래스를 리턴한다.

     

     

    이렇게해서 클래스를만드는 makeClass 함수를 완성했습니다.

    이제 어떻게 사용될 수 있는지 사용 예제를 보도록 하겠습니다.

    var person_obj = {
      _init: function(){
        console.log("person init");
      }
      getName: function(){
        return "Person Name : " + this._name;
      }
      setName: function(name){
        this._name = name;
      }
    };
    
    var student_obj = {
      _init: function(){
        console.log("student init");
      },
      getName: function(){
        return "Student Name : " + this._name;
      }
    }
    
    var Person = makeClass(person_obj);  // Person 클래스를 정의
    var person = new Person();	// 1) person init 출력
    person.setName("junyoung han");	
    console.log(person.getName());	// Person Name : junyoung.han 출력
    
    var Student = Person.makeClass(student_obj);	// Person을 상속받는 Student 클래스 생성
    var student = new Student();	// 2) student init 출력
    student.setName("student junyoung han");	// 3) Person의 setName함수를 상속받아 실행
    console.log(student.getName());	// Student Name : student junyoung han 출력
    
    console.log(Person.toString());	// 4) Person이 Function을 상속받는지 확인
    

    person 객체와 student 객체로 클래스를 만들어 사용하는 모습입니다.

    자바와 비슷한 것 같아요!

     

    다음과 같은 내용을 다시 한번 짚고 넘어갔으면 좋겠습니다.

    - 생성자 함수가 호출되는가?

    - 부모의 메서드가 자식 인스턴스에서 호출되는가?

    - 자식 클래스가 확장 가능한가?

    - 최상위 클래스인 Person은 Function을 상속받는가?

     

     

    정리하면서..

     

    ES6에 클래스 문법이 추가되었죠.!

    그 영향으로 실무에서도 위처럼 구현할 일은 거의 없을 겁니다.

    그러나 그 class 마저도 내부 구현은 위처럼 프로토타입 체이닝과 생성자함수를 이용해서 되어있지요.

    로직을 구현하는 데에 필요없는 이론일 수는 있겠지만,

    위의 코드들을 이해한다면 자바스크립트의 프로토타입 체이닝과 동작방식을 이해하고 있으므로

    잘못된 코딩과 그에 상응하는 예상치 못한 오류의 시행착오들을 줄여 줄 수 있을거라 믿습니다.

    댓글

Designed by Tistory.