Programming/JavaScript

[JavaScript] 클로저에 대해 알아보자

top@tier 2020. 7. 24. 23:24
728x90
반응형

다음 예제를 보자.

function object(a, b){
    var x = 10;
    var innerFunc = function(innerParam){ console.log((a + b) / (innerParam + x))};
    return innerFunc;
}

var pointer = object(2, 4);
pointer(2);

책에 보면 클로저를 설명하기 위해 실행 컨텍스트, 변수 객체, 스코프 체인, 스코프 프로퍼티 등등 처음보면 이해하기 어려운 내용들이 마구마구 나오게 된다.

이런 개념들을 알면 좋지만 몰라도 위 예제를 쉽게 이해할 수 있게 설명해보겠다.

 

전역 컨텍스트에서 object(2, 4)함수를 호출해 pointer 변수에 담고 pointer를 실행하고 있다. 하나 하나씩 나눠서 보도록 하자. 

object(2, 4)는 2와 4를 파라미터로 넘기기까지 한 함수(객체)이고 함수 내부에서 변수 x, innterFunc만 담은 함수(객체)라고 생각하자. 함수도 객체다. 그리고 object(2, 4)을 pointer 변수에 담는데 pointer변수에 위 객체의 메모리 주소를 담는 것이다. C언어를 배운 사람이라면 pointer 변수가 포인터 역할을 한다고 이해할 수 있을 것이다. 

 

pointer 변수에 (2)를 붙여 호출한다. 

결과값이 어떻게 될까?

우선 object 함수 a, b 파라미터는 2, 4로 넘어가고 pointer(2)를 실행할 때 x에는 10, innerParam에 2값을 넘김으로써 console창에는 (2 + 4) / (2 + 10), 즉 0.5가 표시된다.

 

여기서 pointer(2)를 실행하기 전까지 pointerobject() 안에 innerFunc를 계속해서 참조하고 있다. 함수객체니까 리턴값을 참조하고 있는 것이다. 

 

C언어에서 포인터를 배운 사람이라면 클로저를 금방 이해할 수 있을 것이다. 

 

그럼 클로저를 사용할 때 유의해야 할 사항들을 알아보자.

다음 예제를 보자.

function object(param){
    var num = param;
    return function(x){
        num += x;
        console.log('num: ' + num);
    }
}
var pointer = object(40);
pointer(5);
pointer(-10);

pointer 변수는 파라미터로 40을 넘기는 object함수 객체를 가리키고 있다. 같은 곳을 포인터로 가리키고 있는 것이다. 그러므로 pointer 변수를 계속 실행하면 num값이 계속해서 변한다. 

 

다음 유의해야할 경우를 보자.

function object(){
    var x = 1;
    return {
        func1 : function(){ console.log(++x)},
        func2 : function(){ console.log(--x)}
    };
};
var pointer = object();
pointer.func1();
pointer.func2();

반환되는 객체에 두 개의 함수가 정의되어 있는데, 두 함수가 모두 같은 변수 x를 참조한다. 그래서 각각의 함수를 호출할 때마다 x 값이 변화한다. 

 

다음으로 루프 안에서 클로저를 사용할 때 주의할 점이다.

function countingSec(x){
    for (var i = 1; i <= x; i++){
        setTimeout(function(){
            console.log(i);
        }, i + 1000)
    }
}

countingSec(3);

이 함수를 실행하면 1, 2, 3이라고 출력될 것 같다. 하지만 결과는 4만 연속 3번 출력된다. 왜냐하면 setTimeout에 들어가는 함수가 i를 참조하는데 이 함수가 실행되는 시점은 countingSec함수 실행이 종료된 이후이고, i 값은 이미 4가 된 상태이기 때문이다.

그렇다면 1, 2, 3으로 출력되게 하려면 어떻게 해야 할까?

function countingSec(x){
    for (var i = 1; i <= x; i++){
        (function(thisI){
            setTimeout(function(){
                console.log(thisI);
            }, thisI * 1000);
        }(i))
    }
}

countingSec(3);

즉시 실행 함수를 실행시켜 i값을 thisI에 복사해서 setTimeout 함수에 넘겨주면 원하는 결과를 얻을 수 있다.

728x90
반응형