'javascript'에 해당되는 글 11건

  1. 2012.04.13 Javascript에서 Scope (2) - 응용 1
2012. 4. 13. 15:30

Javascript에서 Scope (2) - 응용

이번에는 지난번에 다룬 기본기를 바탕으로 하여서 OOP개념에 기초가 되는 간단한 Class와 Object를 만들어 보았습니다.


#1. 아주 기본적인 Counter

function MakeIncCounter(initialNumber)
{
     return function() { return ++initialNumber; };
}

counter1 = MakeIncCounter(20);
counter2 = MakeIncCounter(10);
counter1();
counter2();
counter1();
counter1().print("Counter1");
counter2().print("Counter2");

> Counter1: 23
> Counter2: 12

1씩 단조 증가하는 Counter를 만들어 보았습니다.
여기에서 재미있게 볼 부분은 initialNumber 부분 입니다. C/C++에서는 함수가 끝나면 함수 내에서 사용하던 변수들이 모두 소멸되지만 Javascript에서는 참조하고 있는 곳이 있다면 함수가 종료 되었다 하더라도 해당 Context의 변수가 소멸되지 않습니다.




#2. 기능이 조금 있는 Counter

앞의 Counter는 단순히 1씩 밖에 더하지 못하는 Counter였습니다. 여기에 기능을 조금 더 추가한다면 다음과 같이 할 수 있겠습니다.

function MakeCounter(initialNumber, steps)
{
     initialNumber = initialNumber || 0;
     
steps = steps || 1;
     
return {
     
     StartingValue: initialNumber,
     
     GetValue: function() { return initialNumber },
     
     Inc: function() { initialNumber += steps; },
     
     Dec: function() { initialNumber -= steps; }
     
};
}

counter = MakeCounter(20, 3);
counter.Inc();
counter.Dec();
counter.Dec();
counter.StartingValue.print("Counter(S)");
counter.GetValue().print("Counter(C)");

> Counter(S): 20
> Counter(C): 17

여기에서는 GetStartingValue와 GetValue의 차이점을 보실 수 있습니다. StaringValue는 MakeCounter()가 return할 때의 initialNumber의 값을 받고, GetValue는 initialValue의 현재 값을 받습니다.



#3. 가독성 높이기

약간의 코드의 가독성을 고려한다면 아래와 같이 작성할 수도 있겠습니다.

function MakeCounter(initialNumber, steps)
{
     initialNumber = initialNumber || 0;
     steps = steps || 1;
     var name;
     var currentCounter = initialNumber;

     
// Methods
     function SetName(value) { name = value; }
     function GetValue() { return currentCounter; }
     function SetValue(value) { currentCounter = value; }
     function GetSteps() { return steps; }
     function Inc() { currentCounter += steps; }
     function Dec() { currentCounter -= steps; }
     function Print() { currentCounter.print(name); }
     return {
          "StartingValue": initialNumber,
          "SetName": SetName,
          "GetValue": GetValue,
          "SetValue": SetValue,
          "GetSteps": GetSteps,
          "Print": Print,
          "Inc": Inc,
          "Dec": Dec
     };
}

counter1 = MakeCounter(100, 20);
counter1.SetName("C1");
counter2 = MakeCounter();
counter2.SetName("C2");
counter2.SetValue(20);

counter1.Dec();
counter1.Dec();
counter2.Inc();
counter2.Inc();

counter1.Print();
counter2.Print();

> C1: 60
> C2: 22


약간 그럴싸한 Class가 만들어 졌습니다. 하지만 Method를 통한 Member variable의 접근은 가능하지만 Property를 통한 접근은 불가능한 상태 입니다. Member variable이 모두 private이라고 생각한다면 문제가 없겠지만 public한 경우도 있을 수 있으니 이를 확장 해보도록 하겠습니다.


#4. This Object 만들기. 

 Object의 안과 바깥에서 공통적으로 접근할 수 있도록 하기 위해서 thisObj라는 Object를 생성하고, 여기에 Properties와 Methods를 추가하는 방법을 택하였습니다.

function MakeCounter(initialNumber, steps)

{

     initialNumber = initialNumber || 0;

     steps = steps || 1;

    

     var thisObj = {};

    

     // Properties

     thisObj.StartingValue = initialNumber;

     thisObj.name = "";

     thisObj.currentCounter = initialNumber;

 

     // Methods

     function SetName(value) { thisObj.name = value; }

     function GetValue() { return thisObj.currentCounter; }

     function SetValue(value) { thisObj.currentCounter = value; }

     function Inc() { thisObj.currentCounter += steps; }

     function Dec() { thisObj.currentCounter -= steps; }

     function Print() { thisObj.currentCounter.print(thisObj.name); }

     var methods = {

          "SetName": SetName,

          "GetValue": GetValue,

          "SetValue": SetValue,

          "Print": Print,

          "Inc": Inc,

          "Dec": Dec

     };

    

     for (var name in methods)

         thisObj[name] = methods[name];

     return thisObj;

}

counter = MakeCounter(100, 20);
counter.Inc();
counter.currentCounter -= 15;
counter.currentCounter.print("Counter");

> Counter: 105


 이제 정말 Class다운 모양을 갖춘 것 같습니다. 아쉽게도 Javascript의 언어적인 한계상 C++등의 Compiler단에서 지원하는 private, protected등의 Encapsulation을 깔끔하게 적용하기에는 다소 무리가 있지만 그런대로 OOP 프로그램을 할 수준은 아닌가 생각이 듭니다. 물론 private member와 private static member를 구현한 유명한 Technique1들이 있는데 별로 권장할 만한 방법은 아닌 것 같습니다..



#5. Class 기능 확장하기.

OOP에서 가장 기본적인 개념 중 하나가 상속(Inheritance) 입니다. 이 것도 좀 흉내를 내본다면 아래와 같이 할 수 있습니다. 새로 만든 ImprovedCounter는 기존의 Counter가 가진 단순 Inc(), Dec()에 parameter를 지정하여서 특정값을 증가하거나 감소 할 수 있도록 하였습니다.

function MakeImprovedCounter(initialNumber, steps)

{

     var thisObj = MakeCounter(initialNumber, steps);

     thisObj.SetName("Improved");

    

     thisObj._superInc = thisObj.Inc;

     thisObj.Inc = function(steps) {

         if (steps == undefined) thisObj._superInc();

         else {

             thisObj.currentCounter += steps;

         }

     }

    

     thisObj._superDec = thisObj.Dec;

     thisObj.Dec = function(steps) {

         if (steps == undefined) thisObj._superDec();

         else {

             thisObj.currentCounter -= steps;

         }

     }

    

     thisObj.Initialize = function() {

          thisObj.SetValue( thisObj.StartingValue );

     }

    

     return thisObj;

}

var imCounter = MakeImprovedCounter(100, 20);
imCounter.Dec();
imCounter.Initialize();
imCounter.Inc();
imCounter.Inc(-10);
imCounter.Print();

> Improved: 110


이상 여기까지 만들어본 Class들은 Scope에 대한 이해를 돕기 위해서 Javascript에서 지원한 prototypethisnew의 개념을 사용하지 않고 만들었습니다. 하지만 실제 프로그래밍을 한다면 Javascript에서 지원하는 좋은 문법들을 적절히 도입하고, Prototype.js와 같은 라이브러리를 사용하는 것이 바람직 합니다. (참조: Defining classes and inheritance - prototype.js)


Javascript로 OOP를 구현하는 데 참조할 만한 글은....

Object Oriented Programming in Javascript
Classical Inheritance in JavaScript
Classes in Jscript - Part I
Classes in JScript – Part II: Instance Properties / Methods & Class Properties / Methods
Classes in JScript – Part III: Class Hierarchy and Data Encapsulation