Notice
Recent Posts
Recent Comments
관리 메뉴

즐겁게, 코드

렉시컬 스코프와 클로저 본문

💬 언어/Javascript

렉시컬 스코프와 클로저

Chamming2 2021. 2. 10. 11:14

클로저(closure). 분명 한번은 들어봤지만 한번도 직접 써본적은 없는 그 문법입니다.

그런데 정말 없을까요? 오늘은 클로저가 무엇인지 한번 알아봅시다.

let myName = "Chanmin";

function printName() {
  console.log(myName); // "Chanmin"
}

printName();

 

실행 결과로 "Chanmin" 이 출력되는 것은 쉽게 예측할 수 있는데, 함수 안에서 어떻게 바깥의 myName 변수를 사용할 수 있었던 걸까요?

바로 우리가 모르는 사이에 클로저가 사용되었기 때문입니다.

 

이번에는 조금 복잡한 예시를 들어 보겠습니다.

function outer(outerValue) {
  return function inner(innerValue) {
    console.log(`outer value : ${outerValue}`);
    console.log(`inner value : ${innerValue}`);
  };
}

const newFunction = outer("outside"); 
newFunction("inside");

// outer value : outside
// inner value : inside

outer 라는 함수가 inner 함수를 리턴한 다음 인자로 "inside" 문자열을 준 것 까지는 괜찮았습니다.

그런데 inner 함수는 "outSide" 라는 값을 어떻게 알고 있던 것일까요?

렉시컬 스코프와 유효 범위

함수가 실행되면 내부 렉시컬 환경과 외부 렉시컬 환경이라는 고유한 유효 범위를 갖고, 이를 메모리에 유지합니다.

("고유한" 을 강조한 이유는 아래에서 설명하겠습니다.)

 

내부와 외부가 가지는 의미대로 내부 렉시컬 환경은 함수 내부에서 사용된 인자라던가 지역 변수를 다루며, 외부 렉시컬 환경은 함수 바깥의 범위를 참조하게 됩니다. 그리고 변수를 사용할 때는 내부 렉시컬 환경을 검색한 후, 찾고자 하는 변수가 없으면 외부 렉시컬 환경으로 이동해 변수를 계속해서 탐색하는 것이 클로저의 원리입니다.

let myName = "Chanmin"; // 함수 외부 -> 외부 렉시컬 환경

function printName() {
  console.log(myName); // 함수 내부 -> 내부 렉시컬 환경
}

printName();

이제 이 코드에 어떻게 클로저가 적용된 것인지 감이 오시나요?

printName 함수는 myName 이라는 변수를 출력하려 하지만 함수 내에서는 myName 이라는 변수가 선언된 사실을 알 수 없습니다.

따라서 상위(외부) 스코프로 이동해 myName을 찾기 시작하고, 거기서 let myName = "Chanmin" 을 발견해 사용하기로 한거죠!

function makeCounter() {
  let count = 0; // 익명 함수의 외부 렉시컬 환경
  return function () {
    return count++; // 내부 렉시컬 환경
  };
}

let counter = makeCounter();
console.log(counter()) // 0
console.log(counter()) // 1

이번에는 다른 예시를 들어보겠습니다.

counter 라는 변수에는 makeCounter 함수가 리턴한 익명 함수의 참조가 저장되는데요, 저장한 함수를 실행할 때마다 외부 렉시컬 환경에 존재하는 count 변수에 접근합니다.

 

하지만, 한 가지 중요한 점을 기억해야 합니다.

function makeCounter() {
  let count = 0;
  return function () {
    return count++;
  };
}

let counter = makeCounter();
let counter2 = makeCounter(); // counter와 counter2는 서로 다른 렉시컬 스코프를 참조합니다.

console.log(counter()); // 0
console.log(counter()); // 1
console.log(counter2()); // 0
console.log(counter2()); // 1

바로 함수가 실행되어 렉시컬 환경이 생성될 때마다 "고유의" 렉시컬 환경을 갖는다는 것인데요, 따라서 counter 함수와 counter2 함수는 같은 makeCounter 함수에 의해 생성되었을지라도 둘은 다른 환경을 보유한 채로 메모리에 남게 되는 것입니다.

 

지금까지는 함수 내에서 정의되지 않은 변수를 사용하는 것이 "전역 변수를 사용한 거였겠지" 라고 생각했을 수도 있지만, 이게 가능했던 이유 뒤에는 클로저가 있었기 때문이라는 사실을 꼭 기억해 주세요. 😁

반응형
Comments
소소한 팁 : 광고를 눌러주시면, 제가 뮤지컬을 마음껏 보러다닐 수 있어요!
와!! 바로 눌러야겠네요! 😆