블로그 이미지
초딩입맛제주아재
하고 싶은 것만 하며 살고 싶다

calendar

1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
2007. 8. 7. 01:35 Programing/HTML/JavaScript/CSS

<script type="text/javascript">
function StopWatch(){
    this.now = new Date();
    this.counter = 0;
    this.timer = null;
}

StopWatch.prototype.start = function(){
    this.timer = window.setInterval(this.counterUp,1000);
}

StopWatch.prototype.counterUp = function(){
    this.counter++;
}

StopWatch.prototype.stop = function(){
    window.clearInterval(this.timer);
    this.timer = null;
    alert(this.counter);
}

var sw = new StopWatch();
</script>

<input type="button" onclick="sw.start()" value="start" />
<input type="button" onclick="sw.stop()" value="stop" />

위에 간단한 스톱워치 객체가 있다.
객체를 생성하고 start()메서드를 호출하면 스톱워치가 작동하고 stop()메서드를 호출하면 스톱워치가 멈추고 경과된 시간을 초단위로 알려준다.

한번 스톱워치를 작동시켜보자.

start 버튼을 클릭하고 잠시 기다렸다 stop 버튼을 클릭해보자.
예상 되는 결과는 화면에 alert창이 뜨고 경과된 시간이 출력이 되는것이다.

결과가 어떠한가?
경과된 시간이 출력 됐는가?

아마도 alert 창에는 0이 출력되었을 것이다.
왜 이런 결과가 나왔을까?
코드가 잘못됐나 싶어 코드를 훑어봐도 도무지 이유를 찾을 수가 없다.
휴~

한숨은 그만 쉬고 차근차근 디버깅을 해보자.
개발자에게 디버깅은 밥 한술 먹고 반찬 한젓가락 떠 먹는것과 똑같지 않은가;;;

결과를 보면 스톱워치 객체의 counter 속성이 변하지 않았다는것을 알 수 있다.

this.counter++;

이 코드는 분명 this=StopWatch 의 인스턴스 변수 counter 를 1증가 시키는 코드다.
그런데 증가가 되지 않았다.
그렇다면 counterUp 메서드가 호출이 되지 않은 것인가?
counterUp 메서드가 제대로 호출이 되는지 확인하기 위해 counterUp메서드에 alert코드를 삽입한 후 다시 스톱워치를 작동시켜보자.

StopWatch.prototype.counterUp = function(){
    this.counter++;
    alert(this.counter);
}


NaN이라는 메세지를 확인했는가?
counter 라는 인스턴스 변수를 인식하지 못하고 있다.

바로 this가 StopWatch 가 아니라는 뜻이다.

그렇다면 우리를 혼란 속으로 빠뜨린 이 this의 정체는 무엇일까?

counterUp 메서드를 다음과 같이 고쳐보자

StopWatch.prototype.counterUp = function(){
    var str = '';
    for(var temp in this){
        str += temp + '\n';
    }
    alert(str);
}

경고창의 내용을 보면 뭔가 낯익은 단어들이 보일 것잉다.
location,opener,status 등등...
감이 오는가?

counterUp 메서드 안에서의 this의 정체는 바로 window객체였던 것이다.

우리는 분명 StopWatch 객체를 생성하고 StopWatch의 메서드인 start 와 counterUp, stop 메서드를 사용했다.
그런데 별안간 window객체라니...

window객체가 출몰하게된 자세한 이유는 여기를 참조하기 바란다.

요약하자면 setInterval 함수에 인자로 넘긴 StopWatch의 counterUp 메서드의 코드는 전달이 되지만 counterUp의 인스턴스는 전달이 되지 않는 다는 것이다.
이 부분에서 '클로저' 에 대해서 한번 검색을 해볼 것을 권한다.(Ajax In Action을 봤다면 대충 알겠지만..)

때문에 setInterval 에 의해 반복 호출 되는 counterUp는 window객체에 귀속 되게 되고
결국 this는 window객체를 가리키게 되는 것이다.

다소 설명이 애매모호하니 위의 링크를 정독 할 것을 권한다.(내 영어실력이 엄청 짧다....ㅡㅡ;)

아무튼 이제 문제는 찾았다.
this가 window 객체를 가리킨다는 것.

그렇다면 어떤 방법으로 이 문제를 해결 해야 할까?
카운터 증가를 담당하는 메서드의 주인이 window 로 넘어가 버렸으니 StopWatch의 counter 는 무의미하다.
대신 window 객체에 counter 를 달아주면 어떨까??

window.counter = 0;

요걸 start 메서드에 한번 넣어 보자.

StopWatch.prototype.start = function(){
    window.counter = this.counter;
    this.timer = window.setInterval(this.counterUp,1000);
}

그리고 stop에서 원래상태로 되돌려주자.

StopWatch.prototype.stop = function(){
    window.clearInterval(this.timer);
    this.timer = null;
    this.counter = window.counter;
    alert(this.counter);
}

자 이제 다시 스톱워치를 작동시켜 보자.

0이 아닌 다른 숫자가 보이지 않는가??

window객체에 임의로 다른 속성을 할당하는게 꺼림찍 하다면 다른 방법을 쓸 수 있다.
위에서 검색해 보길 권했던 '클로저'를 이용 하는 방법이다.

이 방법은 코드를 좀 많이 고쳐야 한다.

function StopWatch(){
    this.now = new Date();
    var counter = 0;
    this.timer = null;

    this.counterUp = function(){
        counter++;
    }

    this.stop = function(){
        window.clearInterval(this.timer);
        this.timer = null;
        alert(counter);
    }
}

StopWatch.prototype.start = function(){
    this.timer = window.setInterval(this.counterUp,1000);
}

counter변수가 인스턴스 변수가 아닌 임시 변수로 바뀌었고
counterUp과 stop 메서드가 StopWatch 객체안으로 들어왔다.

위와 같이 StopWatch 함수 안에서 메서드를 정의 하면 클로저라는 것이 자동으로 생성 되어 counter 라는 변수와 counterUp과 start 메서드간에 연결 고리가 만들어진다.
때문에 비록 지역변수인 counter 라도 StopWatch 객체가 살아 있는한 StopWatch 객체와 그 생명을 함께 하게 된다.

마무리를 해야 하는데 결론을 내기가 힘들다.
좀 복잡한 내용이고 나도 대충 이해만 한것을 풀어 쓰려니 읽는 사람들도 이해하기가 힘들 것 같다.
중간 내용은 다 잊어 버리고 처음과 끝만 보는게 그나마 이해하는데 도움이 될지도 모르겠다. ㅡㅡ;

좀더 자세한 설명을 원하는 분은 위에 링크된 MDC(Mozila Developer Center)와 Ajax in Action(부록B)를 꼭 읽어 보시길 바라며 나는 도망가야겠다;;;

혹, 이 포스팅을 보고 이건 이게 아니라 정확히 이거다~ 라고 설명해 주실 수 있는 분은 꼭! 가르침을 주시길 부탁드리며...

 

posted by 초딩입맛제주아재