해당 포스팅은 위키북스의 모던 자바스크립트 Deep Dive라는 책을 독학하며 기록하는 글입니다.

다음의 코드의 실행결과를 생각해 보자.

cosnt x = 1;

function outerFunc() {
  cosnt x = 10;
  innerFunc();
}

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

outerFunc();

위 코드의 실행결과는 1이다.

x를 호출하는 함수인 innerFunc함수를 outerFunc함수 내부에서 x를 10으로 선언한 뒤 호출했는데 왜 1이 출력되었을까? 이는 자바스크립트가 렉시컬 스코프를 따르는 프로그래밍 언어이기 때문이다.

렉시컬 스코프와 [[Environment]]

자바스크립트 엔진은 함수를 어디서 호출했는지가 아니라 함수를 어디서 정의했는지에 따라 상위 스코프를 결정한다. 이를 렉시컬 스코프(정적 스코프)라고 하는데, 위의 코드를 보면 innerFunc의 상위 스코프는 outerFunc이 아니라 전역 스코프라는 것이다.

이렇게 함수 자신이 어디서 정의되었는지를 담고 있는 내부 슬롯은 함수 객체의 내부 슬롯인 [[Environment]]이다. 함수 객체는 자신이 존재하는 한 [[Environment]]에 담겨있는 자신이 정의된 렉시컬 환경의 참조를 기억한다.

클로저

const x = 1;

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

const innerFunc = outer();
innerFunc();

위의 코드를 보면 innerFunc에 outer함수의 return값을 반환하고 outer함수의 생명주기는 끝난다. 그럼 innerFunc함수는 console.log(x);라는 내용을 가지고 있고 해당 함수를 실행시키게 되면 첫 행에 선언된 const x = 1;에 의해 1이 출력되어야 할 것 같지만 실제로는 이미 생명주기가 끝나 실행 컨텍스트 스택에서 제거된 outer함수에서의 변수인 x가 그대로 참조되어 outer함수 내부에 있는 const x = 10;에 의해 10이 출력된다.

이렇게 외부 함수보다 중첩 함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변수를 참조할 수 있고 이러한 중첩 함수를 클로저라고 부른다.

하지만 지금까지 배운 모든 함수들은 상위 스코프를 기억하고 있으므로 이론적으로 모든 함수는 클로저지만 일반적으로 모든 함수를 클로저라고 부르지 않는다. 클로저라고 부르는 함수는 중첩함수가 외부 함수보다 오래 유지되면서 중첩 함수가 상위 스코프의 식별자를 참조하고 있을 때를 말한다.

profile
I Will be Relaxed Person

0개의 댓글