Section 12. 자바스크립트 렉시컬과 클로저
I. 렉시컬(정적) 스코프 lexical(static) scope
- 변수나 상수가 코드상 어디에서 지정되었는가에 따라 그 사용 범위를 결정한다.
- 함수가 코드상 어디에서 정의되었는가에 따라 그 상위 스코프를 결정한다.
- 호출한 곳을 기준으로 하는 동적 스포크 dynamic scope와 상반되는 개념이다.
const x = 1;
const y = 1;
const z = 1;
function func1 () {
const y = 2;
const z = 2;
console.log('2', x, y, z); //
func2();
}
function func2 () {
const z = 3;
console.log('3', x, y, z);
}
console.log('1', x, y, z)
func1();
//1 1 1 1
//2 1 2 2
//3 1 1 3
func2을 호출한 블록에서의 y 값은 2이다.
func2이 정의된 블록에서의 y 값은 1이다.
정의된 블록을 기준으로 상위 스코프의 값이 사용된다.
func2를 func1 안으로 옮기면 다음과 같다
const x = 1;
const y = 1;
const z = 1;
function func1 () {
const y = 2;
const z = 2;
function func2 () {
const z = 3;
console.log('3', x, y, z);
}
console.log('2', x, y, z);
func2();
}
console.log('1', x, y, z)
func1();
//1 1 1 1
//2 1 2 2
//3 1 2 3
func2의 y값은 당연히 2가 된다.
II. 렉시컬 환경 lexical environment
- 전체 문서, 함수, 블록을 실행하기 전 만들어지는 내부 객체이다.
- 각 스코프의 고유 값들과 외부 스코프에 대한 참조를 포함한다.
구성요소
- 환경 레코드 environment record - 해당 스코프의 데이터들을 말한다.
- 외부 렉시컬 환경에 대한 참조 outer lexical environment reference
const x = 1;
const y = 1;
const z = 1;
function func1 (a) {
const y = 2;
const z = 2;
function func2 (b) {
const z = 3;
console.log('3', x, y, z, b);
}
console.log('2', x, y, z, a);
func2(a + 1);
}
console.log('1', x, y, z)
func1(1);
자바스크립트에서 엔진이 이런 코드를 읽어 들여서 실행하기 전에 각 스코프마다 렉시컬 환경이란 것이 만들어지게 된다.
func1에서 보면 전역 렉시컬 환경이 외부 렉시컬 환경이다. 따라서 func1의 렉시컬 환경에서 x라는 것이 없으면 바로 인접한 전역 렉시컬 환경에서 x가 있는지 확인하고 있으면 전역 렉시컬 환경에서의 x를 끌어다 쓴다는 것이다.
func2 또한 마찬가지이다.
III. 클로저 closure
- 내부 함수에서 외부 함수의 값에 접근 할 수 있다는 개념 (함수 중첩시)
function func1 () {
const word = 'Hello';
function func2 () {
console.log(word);
}
return func2;
}
const logHello = func1();
logHello();
//Hello
logHello에는 func1 안의 함수인 func2가 반환되어 지정된다.
그런데 출력문을 보면 func1의 실행이 끝났음에도 불구하고 Hello가 출력이 됐다.
어떤 함수가 실행될 때(func1) 자기가 가지고 있던 값을 사용하는 또 다른 함수를 만들어지고(func2) 그것을 외부로 반환하게 되면(logHello) 함수가 종료가(func1) 되도 그 스코프 내의 값은 계속 망령처럼 살아 있게 된다.
func2와 func2가 선언된 환경(func1의 스코프)의 조합을 클로저라고 한다.
function createCounter (start) {
let num = start;
return function () {
console.log(++start);
return start;
}
}
const count = createCounter(10);
count();
createCounter 함수 안에 변수가 선언이 되어 있고 그것을 또 다른 함수가 사용하고 있다. 때문에
const count = createCounter(10); 이 줄을 실행한 시점에서 스코프가 종료가 되어 사라져야 하지만 사라지지 않는다.
따라서 count()를 할 때마다 값이 출력되는 것을 볼 수 있다.
단지 값을 복사해서 갖는 것이 아니라, 해당 값이 저장되는 외부 환경 자체가 유지된다.