JavaScript

클로저

차돌박이츄베릅 2023. 5. 28. 15:41

클로저(Closure)

  • outer는 이 함수가 실행될 때의 렉시컬 환경을 갖고 있음
    즉, 이 함수가 실행될 때 당시의 변수 정보(당시의 outerFunc() x는 10이었음)나 이런 것들이 들어있다
  • 함수가 선언된 렉시컬 환경!!!
  • 함수가 선언될 당시의 외부 변수 등의 정보!!

 

렉시컬 스코프

1. JS 엔진은 함수를 어디서 '호출' 했는지가 아니라
어디에 '정의'했는지에 따라서 스코프(상위 스코프)를 결정한다.

 

2. '외부 렉시컬 환경에 대한 참조값' => outer
함수 정의가 평가되는 시점!!!에 결정된다

const x = 1;

// outerFunc내에 innerFunc가
// '호출'되고 있음에도 불구하고
// 선언은 밖에서 했기 때문에 scope를 공유하지 않음

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

// innerFunc와 outerFunc는 서로
// 다른 scope를 가지고 있다!!!!!
function innerFunc() {
    console.log(x); // 1
}

outerFunc();

 

3. 중첩 함수를 종료했는데 여전히 참조한다 - 클로저 개념

const x = 1;

// 1
function outer() {
    const x = 10;
    const inner = function () {
        console.log(x);
    };
    return inner;
}

// outer함수를 '실행'해서, innerFunc에 담죠
// outer함수의 return부분을 innerFUnc에 담는다는 얘기
// const innerFunc = function () {
//    console.log(x);
//} 으로 갈아 낀다는 얘기
const innerFunc = outer();
// 여기서는 outer함수의 실행컨텍스트는 날라감. 없어짐.
// outer함수는 innerFunc에 값을 담고 그냥 끝나버리는거.
// 그래서 outer컨텍스트는 inner컨텍스트에 담고나서 그냥 없어지는거
innerFunc(); // 갈아낀거를 실행.

 

 


클로저의 활용

  • 클로저는 주로 상태를 안전하게 변경하고 유지하기 위해 사용
  • 상태를 안전하게 은닉한다
  • 특정 함수에게만 상태 변경을 허용

 

[예제 - 클로저 적용 전]

// 카운트 상태 변경 함수 #1
// 함수가 호출될 때마다 호출된 횟수를 누적하여 출력하는 카운터를 구현해요!

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

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

console.log(increase());
// num = 100; // 치명적인 단점이 있어요. num은 은닉(보호)되지 않았기 때문에 중간에 외부 바깥에서 접근할 수 있어서 위험
console.log(increase());
console.log(increase());

// 보완해야 할 사항
// 1. 카운트 상태(num 변수의 값)
// => increase 함수가 호출되기 전까지는 변경되지 않아야 함
// 2. 이를 위해서 count 상태는 increase 함수만이 변경!
// 3. 전역변수 num 요놈이 문제다. -> 지역변수...? 지역변수로 선언하면 변경은 방지했지만 increase()가 호출될 때 마다 num이 초기화되는 이상한 코드가 되어보렸따.. !
// *의도치 않은 변경은 방지하면서 + 이전 상태를 유지해야 함
// 그 해답이 클로저
 
[예제 - 클로저 적용 ]
// 카운트 상태 변경 함수 #클로저 적용

// 클로저의 모습 (function() {})()
const increase = (function () {
    // 카운트 상태 변수
    let num = 0;

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

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

/*
increase()자체는 실행하고
function () {
        return ++num;
    }이 됨. 근데 얘는 항상 외부 환경의 넘버(변수num)를 참조하고있기 때문에 가비지 컬렉터가 가져가지 않고 계속 상태가 유지되는 것. 이게 클로저의 개념


*/

// [코드 설명]
// 1. 위 코드가 실행되면, '즉시 실행 함수'가 호출!! -> 함수가 반환(inner) -> increase에 할당된다
// 2. increase에 할당된 함수(return function () {return++num;};)는 자신이 정의된 위치에 의해서 결정된 상위 스코프인 즉시 실행 함수의 '렉시컬 환경'을 기억하는 클로저다. 즉 let num = 0; 얘를 기억한다. 그래서 계속 실행해도 값이 유지된다
// 3. 즉시 실행함수는 --> 즉시 소멸된다. (outer함수가 불리자마자 callstack에서 바로 popup되는거랑 비슷)
// 4. *결론: num은 초기화가 안됐음. 외부에서 접근할 수 있는 은닉된 값도 얻게 됐음. 의도되지 않은 변경도 걱정할 필요가 없다. --> increase에서만 변경할 수 있기 때문에..!

'JavaScript' 카테고리의 다른 글

숙제2 - 반복문, 조건문 연습하기  (0) 2023.05.28
숙제1 - 문자열 연습하기  (0) 2023.05.28
Class  (0) 2023.05.28
비동기 작업의 동기적 표현 - Promise, Generator, Async/await  (1) 2023.05.28
콜백 함수  (0) 2023.05.28