티스토리 뷰

언어적인 기능으로 보면 Javascript는 완벽하게 private, public, static을 구현한다. 게다가 구현 후에 작동방식도 거의 비슷하다.

1. private 프로퍼티, 메쏘드의 선언
     ClassA=function(){

          var a=30;

          var test=function(){

                return a;

          }

     }


위의 예처럼 클래스 내부에서 var를 통해 생성한 메쏘드 혹은 프로퍼티는 내부에서만 호출할 수 있는 private이 된다.

당연한 얘기지만 Javascript의 구조상 private로 선언된 프로퍼티나 메쏘드는 prototype을 통해 상속했든 소유로 상속했든 다른 객체에서 불러낼 수 없다. 따라서 프레임웍을 짤 작정으로 부모클래스를 만들 생각이라면 var로 선언해서는 안된다(자식이 전혀 이용할 수 없다)


2. public 프로퍼티, 메쏘드의 선언
     ClassA=function(){

          this.a=30;

          this.test=function(){

                return this.a;

          }

     }

public의 경우는 this. 키워드를 이용하면 손쉽게 구현된다. public는 외부에서 자유롭게 호출할 수 있을뿐만 아니라 상속한 자식객체에서도 불러쓸 수 있다. 자식객체가 public 프로퍼티나 메쏘드를 덮어쓰면 아무런 통지 없이 override된다. 따라서 public으로 선언된 모든 프로퍼티와 메쏘드는 override키워드가 들어가 있는 것이나 다름없다.

 


3. static 프로퍼티, 메쏘드의 선언
     ClassA=function(){

     }

     ClassA.a=30;

     ClassA.test=function(){

           return ClassA.a;

     }

클래스 선언 외부에서 프로퍼티와 메쏘드를 추가하게 되면 이는 Static이 된다. 다른 OOP언어들과 마찬가지로 static메쏘드 내에는 오직 static프로퍼티나 전역속성의 값만 포함할 수 있다.


4. 사용시 이슈가 되는 부분


    - private을 상속시키고 싶을 때
       private는 오직 해당 클래스 내부에서만 유효하다. 따라서 자식객체도 사용할 수 없다(이건 바꿀 수 없는 현실..)
       여기에 대해 나는 '__' 이름규칙을 암묵적으로 사용하고 있다.

       ClassA=function(){

            this.__a=30;

            this.__test=function(){

                  return this.__a;

            }

       }

       선언 자체야 public이 되어 당연히 상속되고 호출도 가능하지만 '__'라는 이름 제약때문에 실수로 호출하지 않는다.

     - private프로퍼티와 public 메쏘드의 조합

       이건 처음부터 아무런 문제없이 동작함으로 걱정없이 아래와같이 사용하면 된다. 하지만 상속이 되지 않는건 언제나 주의!
       ClassA=function(){

            var a=30;

            this.test=function(){

                  return a;

            }

       }
       예컨데 classA에서는 전혀 문제없지만 classB.prototype=new ClassA(); 한 경우 test()를 하게 되면 a가 존재하지 않아 에러!

 

     - 부모의 public 속성이 자식에게 공유되는 문제

       매우 심각한 문제로 반드시!! 회피방안을 생각해둬야한다. 
       내장객체, 사용자정의객체가 프로퍼티에 할당되면 값이 아니라 레퍼런스(포인터)가 잡혀 별도의 자식 instance에서 공유하게 된다.
       ClassA=function(){

            this.a=new Array();

       }

       ClassB=function(){

            this.set=function(val){

                   this.a.push(val)

            }

            this.get=function(){

                   return this.a.length;

            }

       }

       ClassB.prototype=new ClassA();
       var test1=new ClassB();

       var test2=new ClassB();

       test1.set();
       alert(test2.get()); // 1을 반환함
       위 소스를 보면 classA에서 선언된 a값엔 Array객체가 들어있는데 자식객체인 ClassB에서는 별도로 선언한 적이 없기 때문에

       ClassB의 인스턴스인 test1,test2에서 a가 잡고 있는 동일한 Array객체를 참조하게된다.
       결과적으로 test1쪽의 a에 push를 했음에도 불구하고 자동으로 같은 객체를 가르치고 있는 test2의 a도 갱신된 꼴이 된다.

       * 해법1 : 객체가 프로퍼티의 값으로 올 때는 반드시 초기화 함수를 부모객체가 지원해야한다. 이것을 보다 일반화하여 init등의 
                    이름으로 통일해두면 좋다. 위의 샘플의 일반적인 해법은 아래와 같다.
       ClassA=function(){

            this.initA=function(){

                   this.a=new Array();

            }

       }

       ClassB=function(){

            this.initA();

            this.set=function(val){

                   this.a.push(val)

            }

            this.get=function(){

                   return this.a.length;

            }

       }

       ClassB.prototype=new ClassA();
       이와 같은 선언을 하면 ClassB가 생성시에 명시적으로 ClassA의 속성을 지정하기 때문에 중복문제가 제거된다.
       * 해법2 : 보다 근원적인 해법은 자식객체가 명시적으로 부모생성자를 호출하는 것이다. call와 apply메쏘드를 통해 기본 지원된다.
       ClassA=function(){

            this.a=new Array();

       }

       ClassB=function(){

            ClassA.call(this);

            this.set=function(val){

                   this.a.push(val)

            }

            this.get=function(){

                   return this.a.length;

            }

       }

       ClassB.prototype=new ClassA();
       훨씬 깔끔하기도 하고 매우 일반적이기도 하다. call과 apply에 대해서는 따로 자세히 쓰지 않겠다.



출처: http://blog.naver.com/hika00?Redirect=Log&logNo=150029057576

댓글