렉시컬 스코프(Lexical Scope) === 정적 스코프(Static Scope)
같은 말이다.
렉시컬 스코프는 함수를 어디에 선언하였는지에 따라 상위 스코프가 결정되는 것을 말한다.
자바스크립트를 포함한 대부분의 프로그래밍 언어는 렉시컬 스코프를 따르며, 정적 스코프(static scope)라고 부르기도 한다.
let x = 1;
function foo() {
let x = 10;
bar();
}
function bar() {
console.log(x);
}
foo(); // 1
bar(); // 1
위와 같은 상황에서 bar()
함수에서 참조하는 x
변수는 bar()
함수의 상위 스코프가 무엇인지에 따라 결정된다. 상위 스코프가 무엇인지 알려면 함수가 선언된 위치를 봐야한다.
위 코드에서 bar()
함수는 전역에 선언되었으므로 상위 스코프는 전역 스코프가 된다.
그래서 bar()
함수 내에 x
변수는 전역에 선언된 x
변수를 참조하게 된다.
특정 코드가 작성, 선언된 환경(위치)을 의미한다. 코드 block
, function
, script
를 실행하기 앞서 생성되는 특별한 객체로, 실행할 스코프 범위 안에 있는 변수와 함수를 프로퍼티로 저장하는 객체이다.
렉시컬 환경에는 총 두가지, 글로벌 렉시컬 환경과 외부 렉시컬 환경이 있다.
전역 코드가 실행되면 글로벌 렉시컬 환경 객체가 만들어지고, 코드를 실행하기 전에 선언되어 있는 변수와 함수를 먼저 글로벌 환경 레코드에 저장한다.
var
로 선언된 변수는 환경 레코드에 변수 이름을 key
로, undefined
를 value
로 하여 초기화된다.
그러나 let
, const
로 선언된 변수의 경우 환경 레코드에 변수 이름을 key
로, <uninitialized>
라는 상태를 value
로 초기화한다.
그래서 이 변수가 선언된 줄에 도달하기 전에 변수를 참조하고자 한다면, var
와 달리 ReferenceError
라 발생한다. 이 개념을 이해하면 호이스팅에 대해 좀 더 자세히 이해할 수 있다.
함수가 선언식으로 선언된 경우 var
로 선언된 변수와 달리 함수 선언식은 함수 이름을 key
로 하고 함수 자체를 value
로 저장하여 완전하게 초기화된다.
var
로 선언된 변수는 선언 줄 전에 접근하면 초기화 값인 undefined
를 리턴하는 반면, 함수 선언식은 초기화 될 때 완전하게 저장되므로 선언 줄 이전에 접근하더라도 의도대로 해당 함수를 사용 할 수 있다.
코드를 실행할 때 필요한 변수를 해당 로컬 스코프와 연관되어 있는 환경 레코드에서 먼저 찾은 후, 찾지 못했다면 렉시컬 환경이 갖고 있는 외부 렉시컬 환경에 대한 참조로 접근하여 찾는다. 이 과정을 반복하며 글로벌까지 도달하여 식별자를 검색하고, 없다면 에러가 발생하는 것이다. ("use strict"
엄격 모드일 경우에만)
function makeCount() {
let count = 0;
return function() {
return count ++;
};
}
let counter = makeCount();
makeCount()
함수가 호출을 시작할 때, 새로운 렉시컬 환경이 만들어진다. 그 안에 count
라는 로컬 변수가 저장된 후, return
문 뒤에 함수까지 counter
변수에 저장된다.
counter
를 실행 시키면 makeCount()
에 안에 있던 count
변수를 참조하여 0이라는 값이 리턴된다. 여기서 클로저의 대한 개념이 나오게 된다.
모든 함수는 [[Environment]]
라는 내부 프로퍼티를 가지고 있다. 이 프로퍼티는 함수가 만들어질 때 그 함수를 둘러싼 외부 렉시컬 환경에 대한 참조를 저장한다.
makeCounter
가 실행되면서 counter
함수는 만들어지고 이 때 makeCounter
의 렉시컬 환경에 대한 참조가 counter.[[Environment]]
프로퍼티에 저장된다. 그리고 counter
함수가 실행 될 때, 비로소 counter
함수의 렉시컬 환경 객체가 생성되고, 이 객체가 외부 렉시컬 환경에 대한 참조를 counter.[[Environment]]
프로퍼티로부터 가져온다. 이러한 과정으로 인해 counter
함수가 언제든 어디서 실행되든 count
변수를 참조 할 수 있게 되는 것이다.