클래스와 모듈

자바스크립트 완벽가이드 클래스와 모듈을 학습한다

  • 향후 ES6에서 다루는 class 키워드를 사용한 개념을 이야기 하는 것이 아니다

  • 무엇을 클래스와 모듈이라 취급하는지 한번 알아본다.

  • ES5 기준으로 설명된다.

  • 클래스는 프로토타입 기반의 상속 메커니즘을 기반한다.

  • 클래스와 프로토타입

    • 두 객체가 같은 프로토타입 객체로부터 프로퍼티를 상속 받았을 때, 같은 클래스와 인스턴스로 취급한다.

    • 동적으로 확장이 될 수 있다.

    • 팩터리 역활을 하는 함수를 이야기 하나 쓸 일은 없을 것 같다.

    • 대부분의 챕터에서 this keyword 의 역활을 확실히 알기를 권장한다. 이 챕터도 마찬가지..

  • 클래스와 생성자

    • 생성자로 내부에 수행할 변수를 초기화, 생성하는 역활을 한다.

    • "(정의한 객체명).prototype" 프로퍼티를 통해 상속 시키고자 하는 코드를 작성한다.

        function Range(from, to){
            this.from = from;
            this.to = to;
        }
        Range.prototype = {
            include : function(){
                //something
            }
            // 배열처럼 상속시키고자 하는 함수를 위와 동일하게 작성해나간다.            
        }
    • new 키워드로 생성자 함수를 호출하며 일반 함수와 차이점은 대문자로 시작해준다가 관례이나 누가 지킬까 싶기도 하다.

    • 썩 와닿지가 않는다.

  • 생성자와 클래스 구별

    • 프로토타입 객체는 클래스를 구별할떄 핵심적인 역활을 한다고 써있다. 같은 프로토타입 객체를 상속한 경우에만 같은 클래스의 인스턴스라고 알아두면 된다.

    • instanceof 는 생성자에 의해서 초기화된건지 검증하지 않고 [프로토타입을 상속하는가 검증]하는 키워드 이다.

    • 별도로 이 연산자에 대해서 공부할 가치가 있어보이긴 한다.

  • constructor 프로퍼티

    • prototype 프로퍼티가 있어야지만 모든 함수는 생성자가 될 수 있다. 그래서 자동으로 설정이 된다.

    • ES5 에서는 Function.bind() 메서드가 반환하는 함수는 자동으로 설정되지 않는다.

    • 자동으로 설정되는 prototype property 의 값은 constructor property 하나만 가진 객체이다.

      이 constructor property 는 열거되지 않고 값으로 해당 함수 객체를 가진다.

    • 참 설명이.. 알아먹기 힘들다.

         var someFunction = function() {};
         var protoTypeInSomeFunction = someFunction.prototype;
         var constructorInProtoTypeInSomeFunction = protoTypeInSomeFunction.constructor;
         console.log(someFunction == constructorInProtoTypeInSomeFunction) // true
    • 어떤 객체에 프로토타입 객체에 constructor property 가 있다는 것은

      이 객체의 생성자를 가르키는 constructor property 역시 상속함을 뜻한다.

      이 사실을 통해서 constructor 를 통하여 클래스를 구별하거나 객체의 클래스를 얻어낼 수 있다는 사실을 알 수 있다.

    • 별도로 정의하는 프로토타입 객체에는 constructor 가 자동으로 생기진 않는다. 이럴경우 명시적으로 설정하여 해결이 가능한다.

        Range.prototype = {
            constructor : Range, //이런 식으로 명시적으로 역참조를 위해 설정할 수 있다.
            //etc...
        }
      • 혹은 이럴 필요가 없이 미리 정의된 프로토타입 객체를 확장해 나가는 식으로 진행하는 것도 하나의 방법이다.

          Range.prototype.etc1 = function(){ //something }
          Range.prototype.etc2 = function(){ //something }
          Range.prototype.etc3 = function(){ //something }
  • 자바 스타일 클래스

    • 궁금했던 정보이기는 하다. 뭔 차이가 있는지 알아보려고 한다.

    • 자바스크립트는 함수를 값으로 가질 수 있다. 그로 인해 메서드와 필드의 뚜렷한 경계선이 없다.

    • 하지만 크게 4가지로 구분 될 수 있다.

      • 인스턴스 필드

      • 인스턴스 메서드

      • 클래스 필드

      • 클래스 메서드

    • 클래스는 크게 다음 3가지 객체가 관련된다.

      • 생성자 객체

        • 클래스의 이름을 정의한다.

        • 이 객체에 추가한 프로퍼티는 클래스 필드와 메서드가 될 수 있다.

      • 프로토타입 객체

        • 모든 인스턴스에 상속되는 객체이며 값으로 함수를 가진 프로퍼티는 인스턴스 메서드가 될 수 있다.

      • 인스턴스 객체

        • 같은 클래스를 통해 생성된 객체는 독립적이며 이 인스턴스 객체에 직접 정의한 프로퍼티는 같은 클래스의 인스턴스라

          하더라도 직접적인 공유는 이루어 지지 않는다.

    • 클래스의 정의 하는 과정의 세단계의 알고리즘은 다음과 같다.

      • 인스턴스 프로퍼티를 설정하는 생성자 함수를 작성한다.

      • 생성자의 프로토타입 객체에 인스턴스 메서드를 정의한다.

      • 생성자 자체에 클래스 필드 혹은 클래스 프로퍼티를 정의한다.

    • 위 클래스를 정의하는 세단계의 알고리즘은 까먹을 수도 있으니 클래스를 정의하기 위한 함수로 작성하여 구현하도록 한다.

      • ex) defineClass(constructor, instanceMethod, classProperties) {}

    • 자바에서는 인스턴스 메서드의 인스턴스 필드를 지역변수 처럼 사용이 가능하고, this 키워드를 별도로 사용할 필요가 없다.

    • 'with'로 비슷한 효과를 내어 흉내낼 수도 있디만 권장되지 않는 점을 알도록하자.

    • 또한 접근제한자 같은 키워드는 자바스크립트에서 존재하지 않기 때문에 비슷한 흉내를 내는 기법도 있다.

      • 모듈 패턴

      • 상수 프로퍼티

      • 클로저 지역변수

  • 클래스 확장하기

    • 자바스크립트의 프로토타입 기반의 상속은 동적이다. 상속받은 프로토타입이 변경이 되면 동일하게 변경이 된다.

    • 가령 모든 객체에서 유용할 것 같아, 최상위 객체 Object 에 정의를 하는 것을 좋지 않은 방법이다.

    • 안전하게 클래스를 확장해 나가고 싶다면 Object.defineProperty() 를 생각해보는 것도 방법이다.

    • 그러나 실무에서는 쓸 것같지 않다. 복잡해질 뿐..

  • 클래스와 자료형

    • 대개는 typeof 연산자를 통해 구분을 하나, 클래스는 독자적인 자료형으로 다루는 것이 권장된다.

    • 핵심 내장객체와 클라이언트 측 호스트 객체는 classof() 함수로 클래스의 속성을 구별할 수 있다.

    • 하지만 평범하게 선언하는 경우 언제나 클래스의 속성은 Object 이므로 도움이 되지 않는다.

    • 다음 3가지로 해당 객체의 클래스를 판단하여보자

      • instanceof

        • 생성자는 클래스를 구별하는 대표적인 수단이나, 구별의 핵심은 프로토타입니다.

        • 생성자 함수를 요구하여 검사하는 연산자 이나, 실제로는 객체가 어떤 프로토타입을 상속했는지 검사하고 생성짜함수를 어떤 것을 썼는지 검증하지 않는다.

        • 위와 같은 사실 때문에 생성자 함수를 검사의 기준을 삼고 싶지 않으면 isPrototype() 을 권장한다.

        • 하지만 이조차 완벽하지 않다. 주어진 객체와 클래스의 관계만 테스트만하고 심지어 특정객체의 클래스가 무엇인지 알수 없다.

        • 더 심각한점, 둘 이상의 화면과 프레임을 사용하는 상황에서 각각은 서로 구분되는 실행 컨텍스트를 가지기 때문에 각각의 전역객체와 생성자함수의 집합을 가진다.

          이름이 같더라도 실행 컨텍스트가 다르기 때문에 같은 생성자의 인스턴스가 아니다!!!

      • constructor property

        • 위 연산자와 동일한 문제를 가지고 있고, 객체에 따라 constructor property 를 사용하지 않는 것도 존재한다.

      • 생성자 이름 함수

        • 생성자의 이름을 함수로 리턴하는 방법을 꾀하는 것도 방법일 수 있으나 이 조차도 함수에 이름이 없는 경우도 존재하기 때문에 좋지않다.

    • 위 세가지로 확인하는 법은 전적으로 만족스럽지 않다.

    • Duck-Typing

      • 앞서 야기된 방법들은 각각의 문제점을 가지고 있다.

      • 위를 해결하기 위해서 나온 대안책

        • 이 객체가 무엇인가? 의 관점에서

        • 이 객체가 할 수 있는 일은 무엇인가의 관점을 가지는 것이다.

      • 위와 같은 접근법은 덕 타이핑이라 불리운다.

      • 새 한마리가 오리처럼 걷고 오리처럼 울면, 그 새는 오리다.

        • 말인 즉슨, 프로토타입 객체를 상속받지 않더라도 그 기능을 수행한다면 해당 클래스로 취급할 수 있다는 점이다.

      • 해당 객체가 어떤 메서드를 가지고 있는지 테스트를 하는 것이다.

      • 구현하는 법

        • 무간섭주의를 준수 하는 것이다.

          • 입력 객체가 필요한 메서드를 구현한다 가정할 때, 이 입력객체가 메서드를 실제로 구현하고있는지 검사를 하지 않는다.

          • 이 가정이 틀릴 시, 존재하지 않는 메서드를 호출한다는 에러가 나타난다.

        • 검증을 하되, 클래스대신 적합한 이름을 가진 메서드가 구현되있는지 검사.

          • 위 방법대로 준수하면 적절하지 않은 입력이 있을 경우 대응이 된다.

          • 에러 메세지에 디테일함을 추구할 수 있다.

        • 덕 타이핑을 위한 예시 함수

                function duckTyping(object){
                    for(var i = 1; i < arguments.length; i++){                 //object 의 각각의 인수에 대하여
                        var arg = arguments[i];
                        switch(typeof arg) {
                            case 'string':
                                if(typeof o[arg] !== "function") return false;  // 접근된 요소가 문자열일 경우 메서드의 이름을 확인한다.
                                continue;
                            case 'function' :
                                arg = arg.prototype;                            // 접근된 요소가 함수라면 프로토타입을 사용하게 한다.
                            case 'object' :
                                for(var m in arg) {                             // 객체라면 모든 프로퍼티에 대해서 검사를 해야한다. 
                                    if (typeof arg[m] !== "function") { continue; }     //객체의 각 프로퍼티에 대해서 검사                                         
                                    if (typeof o[m] !== "function") { return false; }   // 메서드가 아니라면 건너뛴다.                                    
                                }                                        
                        }
                    }
                }
        • 위 함수는 객체의 특정 이름의 함수 프로퍼티가 있는지 검증만 한다.

        • 함수가 어떤 역할을 하는지, 인자값의 개수는, 무슨 자료형인가는 검증을 안한다.

        • 원래 덕 타이핑이란 그런 것이라고 한다.

        • 심도 깊은 검증을 하는 함수 대신 덕 타이핑을 사용하는 함수를 사용하면 더 유연한 api를 제공하는 것이다.

          하지만 이는 사용자에게 책임을 떠넘기는 행위가 되버린다.

        • 이너클래스에 대해선 동작을 하지 않으니 구지 하고싶다면 Object.getOwnPropertyNames() 같은 함수를 사용한다.

  • 자바스크립트의 객체지향 기법, ES5 클래스

    • 많기도 많고, 데이터에 대한 가공은 java 에서 주로 하고 있기때문에 구지 이곳에 이 관점을 더 이상 생각하고 싶지않다. Node.js 라면 모를까..

    • 열거형은 제법 쓸만할 것같다.

Last updated