Javascript – scope 이야기 클로저(Closure) – 내가 보는 클로저 1/2

Javascript – scope 이야기 : 클로저(Closure) #1/2
클로저 함수는 외부 함수의 실행이 끝나더라도 외부 함수 내 변수를 사용할 수 있다.
클로저는 이처럼 특정 데이터를 스코프 안에 가두어 둔 채로 계속 사용할 수 있게하는 폐쇄성을 갖는다.
(구글에서 검색한 결과중 가장 간결하게 표현한 글이어서 퍼왔다.)

간단히 살펴보는 변수의 일생
클로저는 Scope 와 밀접한 관련이 있기때문에 javascript 를 떠나 프로그램에서 변수의 scope 에 대한 개념이 있어야
이해 하는데 조금 수월한거같다. scope 에 대한 이야기를 하자면 너무 길어지니 ,
간단한 코드를 보면서 scope 에 대한 개념만  살펴보고 가자
function myFunction(s)  // 외부함수
{
       var  _name;
      _name =  `i am a ${s}`;
}

var isName = myFuncton(“dog”);

console.log(myFunction(isName));  //  i’m a dog 를 출력한다.
console.log(_name);  // undefined 오류 발생  / 너무 당연

너무 간단한 함수이다. 하지만, 함수 보다 지역변수 _name 에 대한  이야기 해보려한다.myFunction 이 호출되기전까지

_name 변수는 어디에도 없는 변수이다.

myFuncton(“dog”);   이 호출됨과 동시에,
myFunction() 이 호출되면서 var  _name  변수가 정의 되어지고 , 호출 되면서 넘겨진
“dog” string 을 _name 에  “i am a ${name}” 을 할당하고 _name 을 리턴,  _name 변수은 소멸되게 된다.  정확한 소멸 시점은

myFunction()  함수의  브레이스가  { ~ } 닫히는
시점이고 그 전까지 변수는 메모리에 그대로 유지된다. (모든 프로그램이 그렇다)

var isName = myFunction(“dog”);

여기서  isName 은  myfunction 내부에 정의 되었던 변수 _name 의 값을 리턴받아
마치 _name  을 그대로 사용하는겉 같은 효과를 볼 수 있다.
하지만 isName 변수는 myFunction()_name 과 완전히 다른 변수라는것을 이미 알고있을 것이다. 이것이 일반적인 변수의

scope 내에서 일어나는 일이다.

그런데 만약, _name 변수를 , myFunction 함수가 소멸됨과 동시에 사라지지않고
외부에서 계속 사용할 수 있는가에 대한 이야기 이다.

클로저(closure)
클로저를  처음 접했을때 단순 함수에서의 지역변수(호출되는  함수의) 리턴 값에 대한 이야기 인줄알았다. 하지만 위에 개념에서 설명하듯

Javascript의 함수내에 정의된 변수가 외부에서 그 변수를 접근할 수 있냐는 것에 대한 것이다.

좀 더 정확히 이야기 하자면, _name 의 소멸에 관한 이야기 이다.

아래 코드를 보자

function myFunction(s)  // 외부함수
{
       var  _name;
      _name =  `i am a ${s}`;
      var fnGetMyName = function() {     // 내부 함수
          return _name;

      }

      return fnGetMyName;
}

// 변수 getFn 은 myFunction 에서 함수로 리턴을 했기때문에 fnGetMyName  함수
// 밖에서도 함수의 역할을 그대로 할 수 있다.

var  getFn =  myFunction(“dog”);
console.log(getFn());

맨 위에 _name 소멸과정을 개념으로 펴보자면,

마지막 console.log(getFn()); 에서는 _name  변수 내용을 출력하는데,,
사실 이 부분은 문제가 발생하거나,  내부 함수 fnGetMyName 에서 변수가 _name

묵시적으로 선언되어 undefined 된 값이 리턴되나? 하는 느낌이 든다.

아니면, 반대로 그냥 당연해 보이는 자연스러운이 느껴질수도있다.

하지면 변수의 scope 개념을 다시 이해한다면 조금 이상하게 느껴진다.

좀 더 자세히 다시 살펴보자 ,

myFunction()
호출시점 var getMyFunction = myFunction(“dog”);   에서 _name 변수가 선언되고 , _name = `i am a ${s}`  이 할당되어 사용이 되어진다.
그리고  myFunction  브레이스가 } 닫히면서 _name 변수(함수 모두)가 소멸되게 된다.
(아다시피, 여기서 내부함수 fnGetMyName() 에서의  외부 함수의 변수 _name 의 접근은 변수 scope 개념상 당연하고  문제 될 것이 없다.)
그런데 myFuntion() 에서 리턴한 fnGetMyName() 함수를 리턴받은 getFn() 함수는
즉, getFn() 함수는 ( getFn()  = fnGetMyname() )  이미 소멸한  _name
값을 출력하고있는 것이다.

 

소멸되지 않은 함수와 변수

1) 내부 함수 fnGetMyName() 가  외부에서  var  getFn =  myFunction(“dog”);
호출시
리턴되어, 외부에서 사용되기 때문에 fnGetMyName() 함수를 소멸 시키지 않는다. 
( getFn()  함수는 fnGetMyName()함수를 참조한다. = 연결되어있다.)

2) _name 변수 또한 내부 함수 fnGetMyName() 에서 사용하고 있기때문에
myFunction() 함수가 끝나는 시점에 _name 변수도 소멸되지않는다.
(_namefnGetMyName() 내부함수에서 사용하지 않으면 소멸된다.)
( fnGetMyName() 함수는 _name 변수를 참조한다. = 연결되어있다)

*  여기서 한 번더 집고 넘어갈 개념
_namefnGetMyname() 함수는 복사되어  getFn() 를 만드는것이 아니라,
서로 참조에 의해 연결되어 있다는 것이 중요하다.
그 원리는  address 로 접근하여 바라보는 참조와 유사하다는 느낌이들었다.
그래서 개인적으로 약간의 레퍼런스 타입의 개념과 비슷한 느낌을 받았다.
(내부적으로 어떤 작동원리를 가지는지 읽을 수 있는 글이 없었다 – 아직까지는
내가알고는 개념에서는 변수가 가지고 있는 메모리상의 주소를 참조하는 참조 느낌을강하게 받았다.)
만약 fnGetMyName()  함수의 구현이 메모리 0x0000:1111 번지에 구현되어 있다면,
getFn()  함수도 똑같이 0x0000:1111 번지 구현 부분을 바라보고 있다는것이다.

getFn() 은 fnGetMyName() 을 참조하고 fnGetMyName() 은 _name 을 참조하고

*이렇듯 외부함수의 변수를 함수(외부)가 소멸한 뒤에서 계속 참조하기 위해서는 내부함수에서는
외부함수의 변수를 접근하는 방식으로 코딩하여야한다.
(아다시피,,  직접 변수를 return 하면 값만 리턴되므로,,,,)

* 전역 변수로 살아 있는 상태로 메모리에 존재하기 때문에 GC 에의 수집되지
않으므로 사용수  정리가 반드시 필요하다 , getFn = null


이제 상단에 정의한 개념을 다시 살펴보고 정리하자

클로저 함수는 외부 함수(myFunction) 의 실행이 끝나더라도 외부 함수(myFunction) 내 변수(_name)를 사용할 수 있다.
클로저(myFunction 함수가 종료하여  변수와 함수가 소멸되지기전 환경이 그대로 유지되는것
– 즉 fnGetMyName( 함수가 소멸되지않고 fnGetMyName() 함수에서 외부함수에 선언된 _name 변수를
사용하는 바람에 그것도,,, 소멸시킬수 없는 상태 = 클로저(폐쇄)상태
)
이 처럼 특정 데이터를 스코프 안에 가두어 둔 채로 계속 사용할 수 있게하는 폐쇄성을 갖는다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다