2차 공부/TIL

24.07.25 클로저

공대탈출 2024. 7. 25. 17:18

클로저 : 함수와 그 함수가 선언된 렉시컬 환경과의 조합

 

const x = 1;

// innerFunc()에서는 outerFunc()의 x에 접근할 수 없죠.
// Lexical Scope를 따르는 프로그래밍 언어이기 때문
function outerFunc() {
    const x = 10;
    innerFunc(); // 1
}

function innerFunc() {
    console.log(x); // 1
}

outerFunc();

출력은 1이 나온다. outerFunc 안에 x가 10으로 할당되어있고, 그곳에서 innerFunc를 실행했기 때문에 10이 출력될 것이라고 생각할 수도 있지만, 

JS엔진은 함수를 어디서 '호출'했는지가 아니라, 어디에 '정의'했는지에 따라 스코프를 결정하기 때문이다.

이를 어휘적 스코프, Lexical Scope라 한다.

 

외부 렉시컬 환경에 대한 참조값 outer은 함수 정의가 평가되는 시점이다.

 

클로저와 렉시컬 환경

 - 외부함수보다 중첩함수가 더 오래 유지되는 경우, 중첩함수는 이미 생명주기가 종료한 외부함수의 변수를 여전히 참조할 수 있다. 여기서 중첩함수가 클로저이다.

외부함수 outer보다 중첩함수 inner가 더 오래 유지되어 outer은 이미 생명주기가 종료되었음에도 불구하고

outer함수의 변수 x =10을 여전히 참조하여 10을 출력한다.

 

일반적으로 위와 같이 outer가 종료되고 중첩함수가 오래 유지되어 종료된 외부함수를 참조할 때 클로저라 한다.


클로저 예제

// 카운트 상태 변수
let num = 0;

// 카운트 상태 변경 함수
const increase = function () {
    // 카운트 상태를 1만큼 증가시킨다.
    return ++num;
};

console.log(increase());
// num = 100; // 치명적인 단점이 있어요.
console.log(increase());
console.log(increase());

1, 2, 3 이 출력된다. 하지만 이건 num 변수가 은닉되지 않았기 때문에 중간에 num=100으로 바꿔버리면 바뀌는 치명적인 단점이 있다.

1. 카운트 상태값 num 이 increase함수가 호출되기 전까지는 변경되면 안된다.

2. 이를 위해 increase함수만이 num값을 변경할 수 있게 해야한다.

3. 전역변수로 선언된 num이 문제다.

 

// 카운트 상태 변경 함수 #2
const increase = function () {
    // 카운트 상태 변수
    let num = 0;

    // 카운트 상태를 1만큼 증가시킨다.
    return ++num;
};

// 이전 상태값을 유지 못함
console.log(increase()); //1
console.log(increase()); //1
console.log(increase()); //1

변수 num을 increase함수안에 넣어 지역변수로 사용하게 했다.

문제는 함수가 호출될 때마다 0으로 초기화시켜 1을 더해 출력하기 때문이다...

의도치 않은 변경은 방지하면서, 이전 상태를 유지해야한다. >>>> 클로저 사용

 

// 카운트 상태 변경 함수 #3
const increase = (function () {
    // 카운트 상태 변수
    let num = 0;

    // 클로저
    return function () {
        return ++num;
    };
})();

// 이전 상태값을 유지
console.log(increase()); //1
console.log(increase()); //2
console.log(increase()); //3

 

// 카운트 상태 변경 함수 #3
const increase = (function () {
    // 카운트 상태 변수
    let num = 0;

    // 클로저
    return function () {
        return ++num;
    };
})();

// 이전 상태값을 유지
console.log(increase()); //1
console.log(increase()); //2
console.log(increase()); //3

// 1. 코드가 실행되면 즉시실행함수가 호출된다. -> 함수가 반환된다.(inner) -> increase에 할당된다.
// 2. increase변수에 할당된 함수는 자신이 정의된 위치에 의해서 결정된 상위 스코프인
//    즉시실행함수의 '렉시컬 환경'을 기억하는 클로저다.
// 3. 즉시실행함수는 즉시 소멸된다.(outer함수가 불리자마자 call stack에서 popup되는 것과 비슷)
// 결론 : num은 초기화 X, 외부에서 접근할 수 없는 은닉된 값! 의도되지않은 변경도 걱정할 필요 없음
// ----> num은 increase에서만 변경할 수 있기 때문에!

클로저를 생각해야할 때는 즉시실행함수와 즉시실행함수에서의 변수, return해주는 함수를 변수에 넣는것

그리고 그 변수를 호출하여 return으로 전해졌던 함수가 정의된 위치를 지정해주어 변수의 안정성을 유지시키는것을 기억하자.