RSS구독하기:SUBSCRIBE TO RSS FEED
즐겨찾기추가:ADD FAVORITE
글쓰기:POST
관리자:ADMINISTRATOR
앞절에서 이미 언급한 부분입니다. Scope에 관한거죠. 못읽어 보신분이라면 언능가서 먼저 읽으시길 바랍니다. 이 강좌를 하면서 Scope의 개념이 없이는 절대 안되는 부분이거든요. 물론 깊게 설명하지는 않았습니다만,
이번 강좌를 이해하는데 많은 도움이 되리라 생각합니다.

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


이러한 함수가 정의되어 있다고 가정합시다. 함수 A는 a라는 지역변수를 가지고 있고 함수 B는 함수 A안에서
존재하고 b라는 지역변수도 가지고 있습니다.
깜짝 질문, 함수 B에서 a라는 변수를 참조할 수 있을까요? 없을까요? 앞장에서 했던 강좌를 유심히 보신분
이라면 쉽게 알 수 있을 겁니다. 답은 "참조할 수 있다" 입니다. 답은 앞장의 강좌를 다시 읽어서 확인하시기 바랍니다.

참고로 위의 구문은 "중첩함수"라고 부르며 Javascript에서의 private을 만들때 사용하는 방법중에 하나입니다. 실제로 해보시면 아시겠지만, 함수 B는 함수 A안에서밖에 호출할 수 없으며 밖에서 함수 A를 가지고 함수 B를 참조하려 해도 안되는걸 아실 수 있습니다. A.B() <- 이런식으로 절대 호출 할 수 없습니다.

그럼 다음 함수를 보시죠.

<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>

이 스크립트의 결과가 어찌 될거라고 예상이 됩니까? 사실 이 함수 자체를 이해 못하시는 분도 있으리라
생각합니다. 절대 무시하는게 아닙니다. 다만 우리는 알게모르게 쉬운 스크립트만을 접하고 이렇게 구조적으로 파헤친적이 없기때문에 어려울 수 밖에 없었던겁니다. 처음부터 Java 나 C#처럼 배웠다면 이렇게 Javascript가 우리를 힘들게 하지 않았을거라고 전 생각합니다. 일단 실행부터 시켜보세요. 어떻습니까? 여러분이 예상했던 답하고 일치합니까? 일치하다면 자신있게 설명을 하실 수 있습니까? 없다면 아래를 봐주세요 ^^ㅋ

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


이제 소스를 해석해보겠습니다. 다들 청심환 하나씩 드세요. ^^
OutFunc는 자신의 지역변수 out을 가지고 있고 중첩함수인 InFunc를 가지고 있습니다. InFunc는 자신의
지역변수인 inner를 가지고 있고 중첩된 함수(nested function이라고 표현합니다) InFunc는 지역변수 inner를 리턴하는군요. OutFunc는 중첩함수인 InFunc를 리턴하구요. 여기까지 이해하셨죠?

그리고 OutFunc를 실행하고 test_1에 담습니다..여기엔 뭐가 담길까요? 그렇습니다. 함수 InFunc가 담깁니다. 그리고 test_2에는 test_1에 담긴 함수를 실행하고 그결과를 담습니다.

그럼 답을 발표하겠습니다. 답은 "i'm a in -------- i'm a out" 입니다.

제가 묻겠습니다. 변수 inner는 어디에 속해이어야 하는 거죠? 네. 함수 InFunc에 속해서 그 Scope안에서만
존재 해야하며 그함수가 종료되었을때, 같이 사라져야할 변수입니다. 그런데 지금의 모습은 어떻습니까?
alert()을 통해서 inner의 값을 찍고 있습니다. 도대체 어떻게 된 것일까요? 위에 정리한 1,2,3은 도대체
어쩌라고 이렇게 결과가 나오는 겁니까?

눈치가 빠르신분은 이미 알고 계셨을 겁니다. 그렇습니다. 이것이 바로 "클로져(Closure)" 입니다.
클로져의 정의는 "다른 함수내에서 내부객체로 생성된 함수 리터럴을 반환하여 호출 프로그램에서 이를 변수로 배정한 것" 이라고 정의하고 있으며 부연설명으로는 "함수가 동작하는데 필요한 데이터 영역 확장"이라고 하고 있습니다.[o'relly - Javascript for web 2.0 5장]

제가 더 덧붙여 설명하면 첫번째  OutFunc()를 실행함으로써 InFunc()함수를 OutFunc()의 안에서 바깥쪽으로
끄집어내어 영역을 변경(확장)하고 두번째 test_1()을 다시 한번 실행함으로써 inner의 값을 직접 호출 할 수 있도록 호출 프로그램으로 영역을 변경(확장)한 것입니다. 이로써 inner라는 변수는 더이상 OutFunc의
InFunc안에 갇혀있지 않고 어디서든지 호출 될 수 있는 전역변수화가 되어버린 것이죠.
여기까지 이해하셨다면 당신은 이미 중급의 문턱까지 온것입니다.
사실 클로져란것을 정확하게 알고 있는 사람은 드물뿐 아니라 "의도하지 않은 클로져의 생성"으로 고통받고
있는 개발자들도 많습니다. 그만큼 어렵고 이해하기 힘든 개념인 것입니다.

결과적으로만 보면 상당히 훌륭해보이고 멋져 보이지만, 사실 내면엔 "메모리 누수"라는 문제점을 안고
있습니다. 메모리 누수에 대해서는 다음 강좌인 "함수-3"에서 자세히 다뤄보도록 합시다.
오늘은 이만 접어야 겠네요..ㅋ 퇴근시간이 가까워 져서리..ㅋㅋ
그럼 다음 강좌까지 다들 건강조심세요.. 더위가 심하네요..


오늘은 저번 강좌에 이어 클로져에 대해서 이어가겠습니다. 클로져에 대해서 개념이 안잡히신 분들을 위해
간략하게 정리하겠습니다.

함수안에 존재하는 지역변수는 지역변수로써 살아야하며, 함수가 죽으면 따라죽을 운명인데, 클로져는 죽어
마땅한 함수의 지역변수를 밖으로 끄집어내어 영역을 변경(확장)하는 것
을 의미합니다. 물론 이한줄이 클로져를 전부 나타낼 수는 없습니다. 하지만 전 이렇게 설명하려 합니다. 다르게 설명해봤자 서로 헛깔릴게 분명
하거든요 ^^ 사람이든 변수이든 자기의 영역에서 벗어나면 안되나 봅니다..ㅎㅎ 왜냐구요? 클로져도 문제가
좀 심각하거든요... 이름하여 "Memory leak(메모리 누수)" 현상 때문입니다.

메모리 누수의 전체적 그림은 중급강좌정도에 보실 수 있겠네요. 여기서는 클로져에 의한 메모리 누수에
대해서만 다루겠습니다. 메모리 누수 모델이 몇가지 있는데 그걸 다 다루자면 제목에서 보듯이 [기초강좌]에서 범위가 벗어납니다. 우리가 좀전에 뭘 배웠습니까? 영역을 벗어나면 문제가 된다고 했지요 ㅎㅎㅎㅎ

자 다시 본론으로 가서, 클로져는 한 내부함수의 지역변수를 함수 리터럴을 통하여 밖으로 꺼내오는 것을
의미한다 했습니다. 그럼 제가 질문을 해보겠습니다. 그 한 내부함수가 종료되면 클로져에 의해 밖으로
튀어나온 변수는 어떻게 될까요? 그렇습니다... 계속 살아 있습니다. 계속 살아있으므로 부모함수가 죽었는데도 불구하고 계속 호출되어 사용됩니다. 나중에 다시 이야기 되겠지만 이것이 "IE에서의 메모리 누수 모델 1"
입니다. 보통 FireFox나 오페라 같은 브라우저는 클로져가 생성되어도 참조값이 0이면 브라우저메모리에서
GC에의해 소멸되게 됩니다. 허나, IE는 그렇지 못하여 문제가 되는거죠. 전 강좌를 열심히 보신분이라면 쉽게 이해하시리라 봅니다. 클로저의 메모리 누수는 이쯤으로 마치겠습니다. 사실 메모리 누수에 대해서는 할말이
무지 많습니다. 허나, 역시 범위를 넘기면 문제가 ㅎㅎㅎㅎ



posted by blankus

--------------------- 펌글 ---------------------


음냥 초보자들이 알기 딱좋네 !!

나두 넘 좋아 나도 초보거등 ~~~ ㅋㅋㅋ
2007/11/21 12:54 2007/11/21 12:54
이 글에는 트랙백을 보낼 수 없습니다
오늘도 무쟈게 덥군요 ^^; 좀전에 회사 사람들하고 잠시 밖에나가 잡담을 하며 바람좀 쐬고왔습니다.
역시 사무실에 일할때보단 밖에서 놀때가 더 좋군여..ㅎ

오늘은 By value와 By reference에 대해서 알아보겠습니다. 날도 덥고하니 조금은 짧게 이야기해봅시다.
사실 이부분은 제가 C를 공부할때 알게되었습니다만, Javascript에서는 크게 신경안쓰고 작업했던 부분입니다. C를 공부하신 분은 아시겠지만, C에는 포인터란 개념이 있습니다. 물론 Javascript에서는 없습니다.
하지만 표면적으로 C처럼 포인터를 핸들링 하지 않는것 뿐이지, 개념은 필요한 부분입니다.

포인터.. 그놈은 이런놈입니다. 보통 우리가 변수를 선언하고 무언가를 그 변수에 담을때, 그때 그 무언가는
메모리에 저장되게 됩니다. 메모리에 저장될때, "나는 메모리의 어디에 위치해있다."라고 알려주는데 그게 바로 "포인터" 입니다. 말그대로 무언가를 가리키는거죠. 그래서 C에선 일반변수와 포인터 변수를 구분해서
사용하는데, Javascript에서 사용하는 변수는 C에서의 일반 변수가 되겠습니다. 포인터 변수는 뭔가 저장된놈의
메모리 주소를 가지고 있는거구요. 일반 변수는 by value가 되고, 포인터 변수는 넓은 의미에서 by reference가 됩니다. 물론 Javascript에 100% 적용되는 설명은 아닙니다.
여기까지는 쉽게 이해가 가실거라 생각됩니다. 아직 부족하다구요? 그럼 더 쉽게 --;

by value의 참조는 변수에 값을 직접할당하는 방식이며 (변수를 박스라고 생각하시면) 박스안에 내용물이
중요한 놈이죠.
by reference의 참조는 변수에 값을 직접 할당하지 않고,(변수를 박스라고 생각하시면) 박스의 위치에
관심이 있는 놈입니다.
내용물에 뭐가 담겨져있고, 그 내용물이 어찌되던간에 그 박스의 위치만 잘 가지고 있으면 되는 놈이죠.
나중에 변수를 call하면 by reference의 참조는 "값을 가져오라고? 그래. 난 박스주소를 알고있으니까, 그박스에 뭐가있는지는 잘모르지만 내용물을 넘겨줄께"라고 행동하게 됩니다.

그럼 Javascript에서는 위의 개념을 어떻게 가지고 있을까요? 이제 소스를 보면서 이야기해보죠.

<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";

</script>
<body>
</body>
</html>


우선 '첫번째 구문'에 대해서 살펴보죠. 아주 쉽습니다. 단순히 문자열을 변수에 담고있고, 그것을 다시 다른 변수에 담았습니다.
그리고 원래 변수의 값을 변경했군요.

'두번째 구문'은 결과적으로 '첫번째 구문'과 동일하지만 다른점이 하나 있습니다. 바로 new String이란 것을 사용해서 변수에 담고있습니다.
'첫번째 구문'에서 ref_1의 타입은 Stirng이지만, '두번째 구문'에서 ref_2의 타입은 Object입니다. 역시 마지막에선 다른 String Object로
값을 변경하고 있네요.

'세번째 구문'은 많이 사용하는 배열입니다. 배열은 자체가 Object인건 아시죠? 역시 이것도 test_3이라는 변수를 만들고 ref_3을 담았습니다.

이상 소스설명은 드렸습니다. 여기까지 이해가 가시나요? 혹시 안가신다면 제게 질문을 하시거나, 구글링을통해 학습이 필요합니다.
이제 제가 질문을 드리겠습니다.

1. alert(test_1)를 실행하면 값이 어떻게 나올까요?
2. alert(test_2)를 실행하면 값이 어떻게 나올까요?
3. alert(test_3)를 실행하면 값이 어떻게 나올까요?

3개의 답을 모두 자신있게 말씀하셨다면, 거기에 답까지 맞으셨다면 축하합니다 ^^*

1번의 답은 "String..."이 출력됩니다. 이유는 String타입의 값들은 by value에 의해서 참조가 됩니다.
다시말해서, test_1은 ref_1의 주소값을 가지고 있는게 아니라 "String..." 자체의 값 즉, value를 가지고 있는겁니다. 그러므로 아무리 ref_1의 값을 변경해도 값에의한 참조(by value)를 한 test_1은 영향을 받지
않는거죠.

2번의 답은 3번을 설명하고 다시 이야기하겠습니다.
3번의 답은 "1, two, three"가 되겠습니다. 왜일까요? 그렇습니다. 둘중에 하나니까 그렇습니다 --;
위에서 값에의한 참조였으니, 이번엔 그반대의 경우겠지요 ^^; 물론 맞는 답입니다만, 정확한 설명을 드리면, ref_3은 Object를 담고있습니다. 그리고 test_3는 ref_3을 참조하지요. 그리고 Object는 by reference에 의해서 참조가 됩니다. 위에서 제가 말씀드린게 by reference가 되면 값을 가지고 있는게 아니라 참조되는 놈의 주소를 가진다고 말씀드렸습니다. 이해가 되시는지요?
만약 값에 의한 참조가 되었다면 test_3는 값이 변경되기전의 ref_3을 참조했기때문에 test_3는 ref_3의 값의 변동에 상관없이 동일한 값을 가지고 있어야함에도 불구하고, 결과는 변경된 값을 가지고 있습니다. 바로 by value가 아닌 by reference에 의해 참조가 되기 때문이지요. by reference로 참조된 변수는 그안에 값이 어떻게 바뀌든지 상관없이 주소값만을 가지고 있으므로 나중에 주소안에 들어있는 값만을 내뱉어 줍니다.
위에 설명 드렸던 부분입니다.

그럼 2번은 결과가 어떻게 될까요? 참으로 애매한 부분입니다. 답은 "hello world"가 출력됩니다
new String으로 생성했으므로 분명 Object입니다, 그럼 by reference에 의해서 참조가 일어나야 정상이지만
결과적으로 그렇지 못합니다 --;
왜그럴까요? 여기에도 무언가가 있기 때문입니다.

바로 Wrapper Object가 범인입니다. Wrapper를 잘모르시는 분을 위해서 잠깐 설명을 드리겠습니다.
Wrapper는 보통 타입을 변경하기위해 사용합니다. String으로 사용하지만 나중의 변환을 위해 new String으로 생성하여 Object로 만들어 내는것입니다. 물론 Javascript에서는 자동 형변환에 의해 크게 사용되지는
않습니다.
다시 본론으로 돌아가서, Wrapper Object는 Object이지만서도 by value에 의해 작동됩니다.
[O'reilly의 Javascript The Definitive Guide 5/E 3장 참고]
(Javascript에서의 Wrapper Object는 String, Number, Boolen이 있습니다. )
그러므로 예상대로 "world"가 출력되지 않고 "hello world"가 출력되는 것입니다.

오늘은 조금 짧게 써보려고 했는데, 말이 길어졌군요 ㅎㅎ 오늘의 주제는 "참조"였는데 만족할 만한
주제였는지 모르겠습니다.
기초강좌라서 약간은 부담이 되는게 사실입니다 ^^; 최대한  쉽게 풀어서 설명하고 싶은데 생각처럼 그렇게
쉽지만은 않군요ㅋ 포스팅을 하다보니 벌써 저녁먹을 시간이네요.. 오늘도 야근모드인
개발자의 비애ㅋ

자 기초강좌가 언제까지 연재 될지 모르겠지만 가끔은 중급강좌도 쓰도록 하겠습니다.
즐거운 저녁식사 되세요~~

posted by blankus


---------------- 트래백 http://www.blankus.net/trackback/5 ----------

위글을 봐서도 알겠지만 ㅡㅡ 뻔히 아는애기 왜하냐고 하면 ㅡㅡ

답이 없지만 서도 ;; 초보자들이 개념만충 하기엔 딱좋은 글이다.

그래서 퍼왔다 !
2007/11/21 12:51 2007/11/21 12:51
이 글에는 트랙백을 보낼 수 없습니다
웅쓰:웅자의 상상플러스
웅자의 상상플러스
전체 (379)
게임 (5)
영화 (2)
기타 (23)
맛집 (5)
영어 (2)
대수학 (3)
형태소 (5)
Hacking (9)
Linux (112)
HTML (48)
Application_developing (48)
Web_developing (102)
Window (11)
«   2007/11   »
        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  
  1. 2016/01 (1)
  2. 2015/12 (3)
  3. 2015/10 (3)
  4. 2015/03 (2)
  5. 2015/01 (4)