js에서 전역 컨텍스트를 제외한 실행컨텍스트는 eval()이나 함수를 호출함으로써 생성된다. 특정 시점에 생성되는 실행컨텍스트는 콜스택에 push되고, 해당 컨텍스트가 종료되면 pop된다. 새로운 실행 컨텍스트가 생성되면 js엔진은 해당 컨텍스트를 위한 정보들을 생성해 실행컨텍스트 객체에 담는다. 컨텍스트 객체에 담기는 정보는 다음과 같다.
lexical environment와 같은 내용을 포함하지만, 최초 실행시의 스냅샷을 가지고 있다. 실행 컨텍스트가 생성될때 variable env가 먼저 생성되고, 이를 복사하여 lexical env를 생성한 다음 lexical env를 주로 활용하게 된다.
아래부터는 lex로 줄여서 작성한다.
컨텍스트의 생성시 variable env와 lex는 동일하다. lex는 variable env를 복사하여 초기화하기 때문이다. 하지만 lex의 값들은 실행 도중에 변경되는 사항이 즉시 변경된다. lex는 environmentRecord와 outerEnvironmentRecord로 구성된다. 아래에서 각각의 요소들을 살펴보자.
실행컨텍스트가 생성되면 lex안의 environmentRecord에 현재 컨텍스트와 관련된 식별자들이 저장된다. 매개변수, 함수, var로 선언된 식별자가 저장된다. 이것을 비쥬얼적으로 알기쉽게 표현한 가상의 개념이 호이스팅
이다.(실제 변수와 함수를 끌어올리는 것은 아니지만, 끌어 올린것과 같이 내부적으로 동작하므로, 이해를 돕기위해 도입한 개념)
실행되기 전에 var로 선언된 변수의 선언과 초기화, let, const 변수의 선언, 함수 선언문으로 선언된 함수의 전체(선언, 초기화, 대입 모두)가 호이스팅된다. 만약 같은 식별자의 함수가 이중으로 선언되어 있다면 나중에 선언된 값이 전체 컨텍스트에서 보존된다.
아래의 실행값을 예측가능하다면 호이스팅 잘 이해한 것.
function a () {
console.log(b)
var b = 'bbb';
console.log(b);
function b () {};
console.log(b);
}
a();
정답은 function b () {}, 'bbb', 'bbb'
함수 선언문으로 선언된 함수는 전체가 호이스팅되므로 첫 문장이 실행되기전에 environmentRecord에 저장된다.
특히 ES5에서 함수를 선언할때 함수 선언문보다 표현식을 사용하는 것이 권장된다. 함수 선언문으로 선언된 함수의 호이스팅 때문이다.
함수 선언문으로 선언된 함수는 전체가 호이스팅되고, 나중에 동일한 함수명을 선언하면 기존의 함수가 오버라이드 된다. 이는 복잡하고 긴 코드를 협업할때 의도하지 않은 오버라이드 발생으로 인한 버그를 야기한다. 함수 표현식으로 선언된 함수는 할당될 때마다 값이 변할 수 있으므로 의도하지 않은 오버라이드가 발생하지 않는다. 전역공간에 함수를 선언하거나 동명의 함수를 중복해서 사용하지 않는 것이 가장 바람직한 방식이지만 실수는 언제든지 발생할 수 있고, 이를 안전한 코드 컨벤션이나 도구를 활용해서 필터하는 장치를 효과적으로 사용하는 것은 개발자의 의무이다. 모던한 IDE나 린터, ES6의 const를 활용하면 안전할 것 같지만, 현업에서 ES5로 작성된 코드를 다룰 수도 있으니.
스코프란 식별자에 대한 유효 범위이다. 특정 스코프 A의 외부에서 선언한 변수에는 접근 가능하지만, A의 내부에서 선언한 변수는 외부에서 접근 불가능하다. 코드를 작성해본 사람은 누구나 알 만한 기본적인 내용이다. 다만 ES5에서는 함수
에 의해서만 스코프가 생성된다. ES6부터는 블록
을 통해 스코프가 생성되지만 블록 스코프에 영향을 받는 변수 또한 ES6에서 추가된 const
, let
으로 선언된 변수로 한정된다.
스코프체인이란 lex의 연결리스트 같은 형태이다. A(B(C))로 중첩된 형태의 실행컨텍스트가 형성되었을 때 C에서 변수 foo를 찾는 절차는 (당연하게도) 다음과 같다.
만약 동일한 이름의 변수 foo
가 A, B, C에 각각 선언되어 있는 경우 스코프체인을 탐색하는 절차 때문에 C는 C의 lex에 있는 변수 foo
에만 접근할 수 있다. 이러한 성질을 활용해서 변수 은닉화
라는 테크닉을 활용할 수도 있다지만 헷갈리게 굳이 이럴 필요가 있나?
전역 컨텍스트의 lex에 있으면 전역변수, 전역변수가 아니면 지역변수다.
가급적 전역변수 활용은 지양하자.
이에 관한 자세한 내용은 3장에서 상세히 다룬다.