ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Closure에 대하여
    Programming/javascript 2022. 2. 10. 21:13

     

    앞서 작성한 Execution Context에 나온 개념을 이해하였다면 해당 개념의 예중 하나인 Closure(이하 클로저)에 대하여 알아보자.

    클로저란, 함수와 함수가 선언된 어휘적인 환경(Lexical Environment)의 조합이라고 보면 된다. 여기서 필요한 개념은 JS가 변수의 Scope를 어떻게 지정하는지를 알아야하는데

    Lexical Environment인 Lexical Scoping에 대하여 알아본다.

     

    우선, 어휘적 범위 지정인 렉시컬 스코핑은 한 함수의 내부에 다시 선언된 함수의 케이스를 생각하면 쉽다.

    function human(){
        //
        let elem = "test";
        
        function name(){
            //
            console.log(elem);
        };
        name();
    };
    
    human();

    과 같은 코드가 있다고 생각하여 보자. 내부에 생성된 내부함수인 name()은 지역변수(local variable)이 존재하지 않지만 부모함수라고 할 수 있는 외부 함수가 elem이라는 지역변수를 선언하였기 때문에 해당 변수에 접근할 수 있다. 만약, 직접 elem이란 이름의 지역 변수를 선언했다면 외부 함수의 변수에 this.elem이라는 명령어를 통하여 접근 할 수 있을 것 이다. 함수와 변수 Hoisting을 고려해본다면 haman()->elem->name()->console.log(elem)참조 순으로 로직의 상단에서 선언되고 초기화 되어 문제 없이 실행 가능 할 것이라는 것을 예상 할 수 있다.

    해당 케이스의 "Lexical "은 어휘적 범위 지정의 과정에서 변수가 어디에서 까지 사용 가능한지 알기 위하여 해당 변수가 로직의 어디에 선언되었는지 고려한다는 것을 의미한다. 중첩된 함수는 위의 예시의 코드 처럼 외부 범위(Scope)에서 선언된 변수에도 접근 할 수 있다.

    다른 한가지 예를 더 보자.

    function human(){
    	//
        let elem = "test";
    
        function name(){
            //
            console.log(elem);
        };
    
        return name();
    };
    var testFunc = hamun();
    testFunc();

    를 실행하면 변수 testFunc에 haman() 함수에서 리턴된 name()함수가 그대로 저장되어 testFunc를 함수 형태로 호출하여도 그대로 실행이 되며 심지어 name()함수의 외부에 위치한 hamun()함수의 지역 변수에도 접근이 가능하다.

    클로저(Closure)는 함수와 함수가 선언된 어휘적 환경의 조합이기 때문에 리턴하는 함수가 클로저를 형성하고 그 시점을 기점으로 유효 범위 내에 있는 모든 지역 변수로 구성된다.

    즉, testFunc는 humun() 함수가 실행 될 때 생성된 name() 함수의 인스턴스에 대한 참조이다. 이때, name() 함수의 인스턴스는 외부 함수인 human() 함수의 지역 변수인 elem이 있는 어휘적 환경에 대한 참조를 유지한다. 그렇기 때문에 testFunc()를 호출되어도 변수 elem은 사용 할 수 있는 상태로 남게되는 것이다.

    개인적으로는 조금 변태같은 사용법이라고 생각하지만 아래와 같은 로직도 가능하다.

    function testFunc(x){
    	//y는 기본적으로 초기화 되어있음.
        let y = 1;
    
        return function(z){
        	//
            y = 100;
            return x+y+z;
        };
    };

    //클로저에 x의 값이 매개변수로 전달, 현재까지 x,y 값이 저장됨

    const testAdd1 = testFunc(5);

    const testAdd2 = testFunc(10);

    //위에서 testAdd1,2 함수에 저장한 testFunc의 리턴 값인 함수의 매개변수(z값)를 지정할 수 있다.

    console.log(testAdd1(2));

    console.log(testAdd2(2));

    //값 107,112

    이때 testAdd1과 testAdd2가 클로저이다.

    클로저는 오용,남용하면 자칫 콜백지옥에 빠질 수 있고, Call Stack의 Execution Stack에 쌓인 함수 메모리의 반환에 시간이 지체되며 전체적인 퍼포먼스가 떨어질 수 있지만 잘만 사용한다면 사용이 되지 않는 함수는 경우에 따라 아예 내부 함수가 클로저를 형성하지 않기 때문에 인스턴스에 대한 참조와 지역 변수들 또한 만들지 않으니 Call Stack에 사용되는 메모리는 아낄 수 있는 양날의 칼과 같은 느낌이라고 할 수 있고, 어떠한 어휘적 환경에서 데이터와 데이터를 조작하는 함수를 연관 시켜주기 때문에(함수 인스턴스) 불필요한 함수를 다중 선언 하지 않아도 되어 유용하다.

     

    Reference Post.

    MDN용어사전-클로저

    댓글

Designed by Kort.