by-nc-sa     개발자, DBA가 함께 만들어가는 구루비 지식창고!

4. 자바스크립트와 객체지향




자바스크립트와 객체지향.

자바스크립트의 runtime

일단 객체지향을 알기 전에 자바스크립트에 대해서 아주 간단히 알고 넘어가 보도록 하자.
기본적인 문법이야 개발자로서 그간 웹에서 간단히 Validation역활을 하는 코드를 작성을 해 보았을테니
넘어가 보도록 하고 그간 개발하면서 부딪치게 된 문제들이나 이상한점에 대해 알아 보도록 하자.
말보다는 빠른이해를 위해 코드를 중심으로 살펴 보자

<html>
 <script>
  function fnc_1(){ alert('fnc_1');}
  function fnc_2(){ alert('fnc_2');}
  function fnc_3(){ alert('fnc_3');}
  
  fnc_1();
 </script>
 <body onload="fnc_2()">
  <script>fnc_3()</script>
 </body>
</html>

음 이 코드를 보고 구조와 표현이 왜 붙어 있어야 하나 올바 르지 않다 스크립트를 분리해라 하실분은 아래코드 가져다가
테스트 하기 바란다. 이부분은 프로토타입.js설명이 아니니 최대한 간단한 소스로 테스트 하면서 넘어 가겠다.

Event.observe(window, 'load', function() {
    ...
    ...
});

일단 처음 코드에서 드리고 싶은 질문은 위의 결과가 어떻게 나올것 인가에 대한 것이다.
1 : fnc_1 -> fnc_2 -> fnc_3
2 : fnc_1 -> fnc_3 -> fnc_2

1번일까 2번일까? 본인은 이 문제를 블랭커스님 홈페이지 에서 보고 풀었다가..틀렸다 _;;
이걸틀리고 나서야 내가 얼마나 기초가 없고 삽질하는 개발자 인지 다시 한번 깨다고, 프로토타입.js를 공부하기전에
일단 기본은 알고 가자 라는 생각에 내용을 다시 정리 하게 되었다.
각설하고...답은 무엇일까?

정리한 내용에 따르자면 정답은 2번 이다. 우리는 답이 중요한게 아니라 그 이유가 중요 하므로 그 내용을 써보겠다
첫번째를 답으로 고른 사람은 다음과 같은 흐름으로 소스코드를 보았을 것이다.

fnc_1은 가장 먼저 실행 된다 왜냐하면 스크립트 언어는 인터프리터 언어이기 때문에 위에서 아래로 순차적으로 해석 되어지고 실행된다. 그래서 fnc_1이 먼저 나온다.
onLoad는 body 가 전부 로드되면 실행된다. 그래서 fnc_1이 실행되고 fnc_2가 실행되고 그다음에 body안에 있는 fnc_3이 실행된다고 생각 한다.

위의 가설은 어디 까지나 틀린것이다 body 는 DOM 객체의 일부 이고 onLoad라는 attribute node는 DOM의 일부인 body 태그가
전부 읽히고 나서 실행된다. 그런데 Why(???) fnc_3이 먼저 실행되었냐면 바로 자바스크립트가 인터프리터 언어이기 ?문이다.
위에서 아래로 순차적으로 읽히고 실행된다. HTML은 DOM으로 해석될수 있지만.. JS는 JS일 뿐이다.
(물론 잘 이해가 안갔을 경우 위에 설명해 놓은 글이 다음과 같이 해석될수도 있다.
onLoad가 먼저 있기?문에 위에 부터 아래로 실행된다면 fnc_2가 나와야 하지 안냐??? 라고 생각 할수도 있지만...
익스플로러는 HTML문서를 DOM으로 보고 그걸 해석해 나갈때 body태그 는 하나의 엘레먼트노드로 인식되겠고 바디태그의 안의 onLoad는 엘레먼트 노드로 보고 onLoad의 Value인 스크립트 fnc_2는 당연히 텍스트 node로 저장 되고 그리고 스크립트 <script> 를 보고 나서
다시 fnc_3를 실행하고 이제 body가 전부 로드 되면 텍스트 엘레먼트인 fnc_2를 실행하게 된다...이해가 잘되었길...안되면 큰일인데.)

위의 예제를 좀더 복잡 하게 적용해서 보자

<html>
 <script>
  fnc_2(); // 황당하지 않는가? 난 지금 이게 가능 하다는게 이해가 안간다. -_-
           // 이 예제를 참조한곳에서는 이걸 <script></script>사이에 존재하는 코드를 한줄로
             // 인식해서 인터프리팅 하기에 가능 하지 않을까 한다고 써놨다.
           // 이부분에 대해서는 이 예제를 만들고 설명한 사람도 정확한 이해가 안되었던거 같다 글에 달린 리플을 모두 적어
             // 놓을 테니 한번 참고 하기 바란다.
           // 1 번해석 : fnc_2를 호출 하는 시간 하고 <script></script> 한구문을 해석하는게 거의 동시에 
             //           이루어졌다 라고 할 수있다.
           // 2 번해석 : <script> 안에서 한번 다 읽고서 (함수내용은 말고 함수명까지만?) 다시 읽는다.
           // 2 번해석의 근거를 들어 보자면 다음과 같다고 한다.
           //
           // 함수는 호출될 때 그 내용을 읽어옵니다 (파싱)
           // function f (a, b) {
           //   return a + b;
           // }
           // 을 실행하는 속도 보다.
           // function f (a, b) {
           //   return a + b;
           //   return a * b;
           // }
           // 을 실행하는 속도가 더 느립니다.
           // 반복문으로 간단하게 테스트하면 알 수 있습니다.
           // 완벽한(?) 인터프린터라면 return 을 만난이상 그 이후 내용에 대해 영향을 받지 않아야 하는 것인데, 
           // 영향을 받는 것 을 보면 함수가 호출될 때 그 내용의 전체를 한번(?) 파싱(?)한다는 것을 추측해볼 수 있습니다.
           // 이 테스트 결과로 미루어보았을 때 <script> 안에서도 함수명들을 한번 읽어내고 
             // (내부적으로는 함수의 이름과 그 메모리 위치 정보같은 것들을 등록시키겠죠.) 인터프린팅을 시작하는 것 같습니다  

  function fnc_5() {
   this.msg = "fnc_5" ;
   this.call = function() { // 이부분은 뒤에 프로토타입 들어 가면 계속 하게 될거다. 
    alert(this.msg);        // 여기서는 그러려니 하고 넘어 가자.
   }
   fnc_4();
   this.call();
  }
  
  function fnc_1(){ alert('fnc_1');}
  function fnc_2(){ alert('fnc_2');}
  function fnc_3(){ alert('fnc_3');}
  function fnc_4(){ alert('fnc_4');}

 </script>
 <body onload="fnc_1()">
  <script>
    new fnc_5();
    fnc_3();
  </script>
 </body>
</html>

이번 코드를 실행 시켰을? 나오는 결과를 예측 하여 보자.
답은 다음과 같다. fnc_2 -> fnc_4 -> fnc_5 -> fnc_3 -> fnc_1

보너스 문제 아래는 될까 안될까??

<html>
 <script> fnc_2(); </script>
 <body>
 </body>
 <script>
  function fnc_2(){ alert("fnc_2"); }
 </script>
</html>

당연히 안된다. 아까 설명된거와 같이 스크립트는<script> 와 </script>를 한번에 처리 단위로 보기에
위 코드는 정의과 호출이 서로 다른 처리단위에 놓여있어 에러다.

자바스크립트의 변수 Scope

어렵게 설명 말고 간단 하게 우리가 보통 쓰는 변수는 전역 변수와 지역 변수 있다 이제 그게 자바 스크립트에서
어떤 특이 상황이 있는지 살펴보고 함정에 빠지지 않게 되도록 하자.

<script>
  var global = "나는 전역변수다";
</script>

<script>
  global = "나도 전역변수다!!";
</script>

보통 전역변수는 위의 2가지 경우가 있다. 차이점은 var가 있는가 없는가의 차이다. 그게 무슨차가 있냐!!라고 나에게
말을 한다면 나도 이번에 알게 된거지만 겁나게 큰 차이가 있다. 우리는 말을 좋아 하지 않는다 코드를 좋아 한다.
다음 코드를 통해 차이점을 알아 보자

1.
<html>
<script>
  msg = "난 전역변수다";

  function fncScope(){
   var msg = "난 지역변수 일까??";
   alert(msg);
  }
</script>
<body>
<script> fncScope(); </script>
</body>
</html>

2.
<html>
<script>
  msg = "난 전역변수다";

  function fncScope(){
   alert(msg);
  }
</script>
<body>
<script> fncScope(); </script>
</body>
</html>

3.
<html>
<script>
  function fncScope(){
    msg = "난과연지역변수일까?";
  }

  function call(){
    alert(msg);
  }
</script>
<body>
<script>
   fncScope();
   call();
</script>
</body>
</html>

자 위와 같은 코드에서 결과는 어떻게 나올지 살펴보자

1. 당연히 '난 지역변수 일까??' 이게 나온다.
2. 당연히 '난 전역변수다' 이게 나오겠지.
3. 그럼 3번?는???

답은 난과연지역변수일까?가 2번 나온다.
왜?? 그럴까 그건 var없이 쓰인 변수는 무조건 전역변수로 선언되기 ?문이다. 반드시 기억 하기 바란다.
그렇기 ?문에 변수 선언에는 무조건 var를 기술 해야 한다. 애매모호한 부분은 반드시 제거 해서 프로그램 하자.

자바스크립트의 call by value 와 call by reference

그러고 보니 학생때 이거가지고 참 많이 싸웠던거 같다..JAVA하면서 말이다.
이지면을 이용해서 다시 한번 하고싶은 말은 친구들아 자바에는 애초에 사용자가 직접 주소를 포인팅 하지 않고 JVM이 관리 한단 말이다.
우리는 Heap에 올라간 밸류의 주소를 포인팅 할수 없다 단지 jvm에서 제공해주는 jvm이 stack영역에 맵핑 해 놓은 주소를 알고 있을뿐이지.머 상관없는 이야기니 다시 본론으로 돌아 가서 자바스크립트에서의 예제를 살펴 보자

<html>
<script>
   //첫번째 구문
   var ref_1 = "string...";
   var test_1 = ref_1;
   ref_1 = "none";

   //두번째 구문
   var ref_2 = new String("hello world");
   var test_2 = ref_2;
   ref_2 = new String("world");

   //세번째 구문
   var ref_3 = new Array("one", "two", "three");
   var test_3 = ref_3;
   ref_3[0] = "1";

   alert(test_1);
   alert(test_2);
   alert(test_3);
</script>
<body>
</body>
</html>

이번 예제를 이해 하는데 나는 자바의 개념이 나를 이해 시키는데 큰 도움을 주었었다. 스크립트 또한 동일한 메모리 모델을 가지고 있고
그러한 방식으로 동작하는지에 대한 확신은 없지만 일단 나온 예제가 거기에 맞아 떨이지기에 그림을 같이 올려 놓겠다.

일단 자바에는 일반 프리미티브 타입과 오브젝트 타입이 있는데.. 위의 예제로 보면 스크립트도 비슷한거 같다.
일반 프리미티브 타입인 경우에는 상관 없지만(물론 자바에서는 스트링 또한 오브젝트 타입이다. 하지만 스트링은 뮤터블 하지 못하다
스트링을? 나머지 오브젝트 타입은 모두 뮤터블 하다... 이건 자바에서 그렇다는 이야기다)위에서 처럼 new String 통해 생성된
객체는 Stack영역에 주소가 할당 되고 Heap 객체가 올라간다. 그러니까 test_2에서도 new를 통해서 새로운 객체가 생성되고 그것을
ref_2가 참조한다. 그리고 원래대로라면 GE(가비지 컬랙션.. 스크립트에도 가비지 컬랙션이 있다..)에 의해 Heap영역의 "hello world"
객체가 처리 되어야 하나. test_2가 가르키고 있어서 메모리에서 지워지지 않고 남아 있을수 있다.
위의 예제는 이렇게 해석 될거 같다.

자바스크립트의 함수의 종류 1

JS에서 함수의 종류는 크게 3가지로 볼수 있다.

종류 선언방법 특징
정적함수(선언적 함수)
function 함수명 (인자1, 인자2, ...., 인자n) { ...처리식... } 
한번 파싱되면 메모리에 상주되어 몇번을 어떻게 호출하던지 동일한 함수객체가 생성됩니다.
익명함수 var fnc = new Function("인자",....,"...처리식...") 직접적으로 함수를 명명하거나 선언하지 않는 함수를 익명함수라고 합니다. 익명함수가 정적함수(선언적 함수)와 다른 차이점은 익명함수는 매번 호출될때마다 다른 함수객체가 생성됩니다. 즉 런타임때 동적으로 생성되어 실행되어 지는것이죠.
리터럴 함수
var fnc = function (인자) {..처리식..} 
생긴 모습은 익명함수와 비슷하게 생겼지만 특성은 정적함수(선언적 함수)와 동일합니다. 즉 익명함수와는 다르게 페이지 로딩시 한번만 파싱되고 메모리에 상주하며 생성된후 몇번을 호출해도 같은 함수객체를 사용합니다.

자바스크립트의 함수의 종류 2

일단 코드부터 보고 시작하자

<html>
<script>
 //중첩함수
 function A(){
   var a = "1";
  
   function B(){
     var b = "2";
   }
 
 }
</script>
<body>
</body>
</html>

일단 위 코드의 주 사용처는 자바 스크립트로 private를 만들때 사용 하는 방법 이다. 왜 private냐면..
함수 B는 A안에서만 호출할수 있고 A를 가지고 B를 참조하려해도 방법이 없기 ?문이다. 이걸이용해서 private 역활을 하게 구현 하는거다.

예제를 보자

<html>
<script>
 function OutFunc(){
   var out = "i'm a out"
   function InFunc(){
     var inner = "i'm a in "+"-----"+ out;
     return inner;
   }
   return InFunc;
 }

 var test_1 = OutFunc();
 var test_2 = test_1();
 alert(test_2);
</script>
<body>
</body>
</html>

위 코드를 제대로 이해 하기 위해서는 다음 3가지가 필요하다. (나같은 경우는 프로토타입.js를 계속 본결과 아무 부담 없이
받아 들여지는 코드 이지만 처음에는 정말 이해가 안갔다.. 어떻게 펑션을 변수에다가 대입을 하는건가 하고...)

1. Javascript에서의 단위는 함수이다.
2. 함수의 지역변수는 함수밖에서는 절대 호출 할 수 없고, 함수내부에서는 전역변수보다 지역변수가 우선한다.
3. 변수는 각각의 Scope를 가지며 자신의 Scope를 벗어날 수 없다.

모두 매처음 저 꼭대기에 써져 있는 내용이다. 자 그럼 위의 코드를 실행해 보면 위 3가지 조건중 마지막에 동의 하지 못하는
결과를 내보낼 거다.... 이게 어떻게 가능 한건가 하면 이를 자바 스크립트에서는 다음과 같이 정의 하였다

클로져(Closure) : 다른 함수내에서 내부객체로 생성된 함수 리터럴을 반환하여 호출 프로그램에서 이를 변수로 배정한 것 이며
함수가 동작하는데 필요한 데이터 영역 확장 이라고도 한다.

이게왜 가능 한가를 위 클로저의 정의에 따라 설명을 하면

OutFunc()를 실행함으로써 Infunc()함수를 OutFunc()의 안에서 바깥쪽으로 끄집어내어 영역을 변경(확장) 하고 두번째 test_1()
을 다시 한번 실행함으로써 inner의 값을 직접 호출 할 수 있도록 호풀 프로그램으로 영역을(확장)한 것이다. 이로써 inner 변수는
더이상 OutFunc의 InFunc()안에 갇혀있지 않고 어디서든지 호출 될수 있는 전역변수화 된다.

고 할수 있다. 자 그렇다면 위에서 나온 내용으로 유추 가능한 내용이 있다
지역변수는 지역변수로 살다가 함수 리턴이 끝난 후에는 죽어야 하는데.. 위의 내용에 따르면 영역이 확장되어 결국 전역변수 처럼
쓰여지게 되는데 이는 결국 메모리 누수의 문제를 일으 키게 된다는 내용이나 마찬가지란 거다.
조심하자 메모리누수..( 이쯤에서 나도 살짝 이행가 안돠는게 prototype.js를 보면 위와 같이 클로저 형태의 처리 구문이
굉장히 많은데.. 메모리 누수의 문제는 걱정 안해도 되는건가? 음 질문을 해봐야 겠다)

자바스크립트의 데이터 타입

음흐흐 이걸 정리 하면서 위에서 궁금 할게 느껸던 부분이 해결 돠었다. 같이 살펴 보도록 하자
위에서 자바스크립트의 데이터 타입이 자바와 비슷 하지 않을까 하는 가정을 하고 진행한 부분이 있었는데.
이부분을 참조 하면 내용이 더 단단하게 이해될수 있을거 같다 각설 하고 내용을 살펴 보자

자바스크립트의 데이터 타입은 3가지로 나눌수 있다고 한다.
1. 원시타입(primitive datatype) - Number, String, Boolean
2. 단순타입(trivial datatype) - null, undefined
3. 혼합타입(composite) - Object

이중에 혼합타입인 Object는 우리가 자주 사용하는 function, array 모두 해당된다.

일단 저위에 원시타입 * (primitive datatype) - String * 을 두고서 자바스크립트의 call by value 와 call by reference
에서는 Object 라고 하지 않냐고 따질 수도 있는데.. 그?는 new 생성자를 이용해서 랩퍼 클래스 처럼 사용 했덜걸 있지 마라

여기까지만 이해되면 위에서 조금 이해가 안되었던 내용이 잘 이해될수 있을거 같다.(나는 그랬다. )

Number - 1) Integer Literals : 다들 아시다시피 10진수 기반의 모든 숫자를 이야기한다.
2) Hexadecimal 과 Octal Literals : 16진수(ex : 0xff or 0Xff)와 8진수(ex : 0377)를 사용하는 number
3) Floating-point Literals : 소수점을 사용하는 number

String - 젤 많이 쓴다. 설명이 더 필요 할까생각 하지만 종종 있는 숫자를 문자로 바꾸는 예제를 들고 마무리 하련다.

  • var n = 123456.789;
  • n.toFixed(0) // "123456"
  • n.toFixed(2) // "123456.78"
  • n.toExponential(1) // "1.2e+5"
  • n.toExponential(3) // "1.235e+5"
  • n.toPrecision(4) // "1.235e+5"
  • n.toPrecision(7) // "123456.8"

반대로 문자를 숫자로 바꾸는 경우의 예제도 살펴보자

  • var txt = "3 blind mice"
  • var num_1 = Number(txt) // undefined
  • var num_2 = parseInt(txt) // 3
  • var txt = "$72.34"
  • var num = parseInt(txt) // return NaN
  • parseInt("11", 2) // 3 이유는 11 이란 값을 2진수로 변경 하기 때문에..

ps. 같은 Number 타입이라도 소수점을 사용하는 것과 그냥 정수형태는 연산속도가 다릅니다
어떤 정수 n을 어떤 정수 m으로 나눈 몫(나머지제외)을 구할때 (n - n%m)/m 이 Math.floor(m/n) 보다 훨씬 더 빠릅니다

이 내용은 다음에 적용 할수 있다. 일반적으로 몫 구하기 연산을 할때 쓰는 형식은 다음과 같다.
parseInt(7/2) 그런데 parseInt가 인자값으로 string을 받기 때문에 자체 계산보다도 형 변환에 시간이 더 걸린다고 한다.
이말인 즉슨 parseInt의 경우 자리수가 길어지면 길어질 수록 더욱 더 느려질수있다는 거다.
그러나 Math.floor는 자리수에 거의 상관이 없는 속도를 보여주므로 이걸 한번 써보는게 어떨가 싶다.

일단 이건 여기까지 하기로 하고 다음에 강좌가 추가 되면 계속 업테이트 하기로 하겠다. GoodLuck

문서에 대하여

  • 문서참고 : 1. 블랭커스님 강좌
  • 최초작성자 : 허용운
  • 최초작성일 : 2007년 1월 6일
  • 이문서는 블랭커스님의 문서에 의존했습니다. 저작권 문제가 발생 하지 않게 원저작자의 링크를 겁니다.

문서정보

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.