[JavaScript] Javascript 핵심개념 알아보기 - callback, 클로저

2020. 8. 4. 00:51강의/JS Flow

1. callback

 

콜백 함수는 위의 그림 한장으로 쉽게

설명할 수 있다. 콜백함수를 제어권을 맡기는 것을 의미한다.

콜백함수를 건네받는 쪽에서는 원하는 작업을 모두 하고 , 넘겨받은 콜백함수를 호출함으로써 그 결과를 알려준다.

그렇다면 제어권의 종류에는 무엇이 있을까

1) 실행시점 

setInterval을 호출하는 것을 생각해보자

setInterval(callback,milliseconds)

setInterval 함수는 콜백함수와 시간을 받아서 , 그 시간을 주기로 콜백함수를 호출하는 함수이다. 

즉, 넘겨받은 콜백함수를 setInterval이 원하는 시간마다 개발자가 시키지 않아도 알아서 실행하는 것이다. 

이때 콜백함수를 통해 어떤 짓을 할지는, 한번 넘겨주고 나면 setInterval이 관리하게 되는 것이다.

 

2) 인자

forEach를 생각해보자

위와 같이 실행하면, 0,1  1,2 2,3 ... 이 출력되거 같지만 사실은 1,0  2,1  3,2  4,3  5,4  가 출력이된다.

왜냐하면 forEach에 콜백함수를 넘겨주기 위해선 forEach가 정의한 형태에 맞춰서 보내줄 수 밖에 없는것이다. 즉, 인자의 내용에 대한 결정권이 forEach에게 있게 된다. forEach에 대한 내용을 MDN에서 찾아보면 콜백함수의 첫번째 인자로 value, 두번째 인자로 index, 세번째 인자로 처리하고 있는 array를 받기로 정의되어있다. 즉, forEach로 콜백함수가 넘어오면 자신이 정의한 인자 순서대로 인식하여 해당 내용을 실행하는 것이다. 

 

콜백함수의 특징

1) 다른함수(A)의 인자로 콜백함수(B)를 전달하면, A가 B의 제어권을 갖는다.

2) 특별한 요청(bind)이 없는 한 A에 미리 정해놓은 방식에 따라 B를 호출한다.

이때, 미리 정해놓은 방식이란 어떤 시점에 콜백을 호출할지, 인자에는 어떤값들을 지정할지, this에 무엇을 바인딩할지 등이다.

 

주의할점은 콜백함수는 '함수'라는 것이다.

 

2. 클로저

클로저라는 개념을 처음 접했을때 , 함수와 매우 헷갈림을 느꼈는데, 이 그림을 통해 어느정도 이해를 하게 되었다. 

클로저는 함수이면서 , 그 함수가 선언될 당시의 lexical environment를 가지고 있다. . .

이게 무슨 말인가 하면, 지난 정리때 배웠듯이 함수는 variable environment와 lexical environment, this binding 기능을 가지고 있다. 

여기서 lexical environment는 다시 두가지로 나뉘는데 하나는 environmentRecord고, 다른 하나는 outerEnvironmentReference이다. 

environmentRecord는 해당 블럭의 식별자들을 모아서 기록해두는 역할을 하며, outerEnvironmentReference는 자신을 품고있는 블럭의 lexical environment에 있는 environmentRecord를 가르키고 참조할 수 있도록 하는 기능을 한다.

 

즉 클로저는 위와같이 말할 수 있다. 그렇다면 위에서 말하는 특수한 현상이 도대체 뭘까 !

 

여기서는 outer()함수가 호출되었을때 inner함수를 리턴하고 outer가 끝이난다. 

하지만 이때 주의해야할점은 리턴된 inner함수가 outer2에 담기게 되는데, 이것이 의미하는 것은 outer2가 기존에있던 outer의 a를 참조하고 있는 상태가 되는것이다. 따라서 outer함수는 호출되고나서 return까지 완료되었는데도, 참조카운트가 줄어들지 않게 된다. (이것은 전역컨텍스트가 종료될때까지 계속해서 살아있게 된다.)

이후 outer2()가 호출되면 outer2는 참조하고 있던 outer의 a에 접근하여 a를 2로바꾸게 되고, 같은 현상으로 밑에서는 3으로 바꾸게 된다.

클로저의 핵심!!

얼핏보면 별거 아닌거처럼 보이지만 이것이 의미하는것은 지역변수가 함수종료 후에도 사라지지 않게 할 수 있따는 것이다. 그것도 선택적으로 말이다. 이러한 클로저가 사용되는 예를 들어보면 웹앱의 경우 초기 세팅시에 이후에도 필요한 값들만 클로저로 만들어 리턴시켜두면, 필요한 값들을 사용하기 위해 새로 초기화를 할 필요가 없어진다. 

클로저가 갖는 장점을 하나 더 말해보자면, 정보를 은닉할 수 있다는 점이다. 

function a() {
  var first = 1;
  var second = 2;
  var third = 3;
  return {
    get a() {return first;},
    set a(v) {first = v;},
    get b() {return second + third}
    set b(v) {throw Error('read only')}
  }
}
var obj = a();

이렇게 a()의 리턴값을 obj에 담으면 , obj는 obj.a(), obj.a(100) 이런 식으로 접근이 가능해진다.

이것은 obj가 함수 a의 지역변수에 접근할 수 있는 것처럼  보인다.

하지만, set b(v)를 호출했을때는 에러를 발생시키기 때문에 obj.b(300)  이런 식의 접근이 불가능해진다. 한마디로 obj는 지역변수에 직접적으로 접근하는 것이 아닌, 클로저의 구현 형태에 따라서만 접근이 가능한 것이다. 이것은 함수의 내부 변수등의 정보를 은닉하는 효과를 갖게된다.