VariableEnvironment(변수 환경)과 LexicalEnvironment(렉시컬 환경)의 차이

VariableEnvironment(변수 환경)과 LexicalEnvironment(렉시컬 환경)의 차이

·

4 min read

VariableEnvironment(변수 환경) LexicalEnvironment(렉시컬 환경)은 JavaScript 실행 컨텍스트의 핵심 구성 요소이다.
그런데 둘 다 변수와 함수 선언을 저장하고 스코프를 관리하는데, 왜 두 개로 나뉘었을까? 🤔

ES6 이전과 이후에 VariableEnvironment와 LexicalEnvironment가 구분된 이유가 다소 다르다.

📌 ES6 이전

ES6 이전에는 두 환경의 구분이 현재처럼 명확하지 않았다.

  • VariableEnvironment와 LexicalEnvironment가 같은 EnvironmentRecord를 공유하지만, 실행 도중 LexicalEnvironment만 변경될 수 있다.

  • LexicalEnvironment는 evalwith 같은 문맥에서 동적으로 변할 수 있지만, VariableEnvironment는 변하지 않는다.

ES6에서 let/const가 도입되면서 두 환경의 역할이 더 명확해졌다.


📌 ES6 이후: let, const의 등장과 스코프 개선

ES6 이후 let, const 가 도입된 이유는 var에 문제가 많았기 때문이다.

문제 1: var는 호이스팅될 때 undefined로 초기화됨 → 의도치 않은 버그 발생 가능

console.log(a); // undefined (에러 아님!)
var a = 10;
console.log(a); // 10
  • 이렇게 하면 "변수 선언 전에 접근하는 것"이 논리적 오류를 일으킬 가능성이 있음 → 예기치 않은 버그를 유발할 수 있음!

  • 만약 개발자가 변수를 선언했다고 착각하고 console.log(a + 1) 같은 연산을 하면, 예기치 않은 버그가 발생할 수 있음.

  • 이를 해결하기 위해 선언전에 접급할 수 없는 letconst가 도입됨.


문제 2: var는 함수 스코프만 가짐 → 블록 스코프({})를 무시함

if (true) {
  var x = 100;
}
console.log(x); // 100 (블록 `{}` 내부에서 선언했지만, 밖에서도 접근 가능!)
  • {} 안에서 선언했는데도 블록 밖에서도 접근 가능 → 블록 스코프가 없음!

  • 이를 해결하기 위해 블록 스코프를 가진 letconst가 도입됨.


문제 3: var는 중복 선언 가능 → 의도치 않은 변수 재정의 발생 가능

var a = 10;
var a = 20; // 에러 없이 덮어쓰기 됨
console.log(a); // 20
  • 같은 이름으로 변수를 선언해도 에러가 발생하지 않음 → 변수를 실수로 덮어쓰는 오류가 발생할 가능성이 높음.

  • 이를 해결하기 위해 letconst는 중복 선언을 금지함.


📌 letconst의 등장: var의 문제 해결!

위와 같은 var의 문제를 해결하기 위해 ES6에서 letconst가 도입되었다.

해결책 1: TDZ(Temporal Dead Zone) 도입 → 선언 전 접근 불가

console.log(a); // ❌ ReferenceError (TDZ)
let a = 10;
console.log(a); // ✅ 10
  • 변수 선언 전에 접근하면 에러 발생 → 논리적 오류 방지!

  • 이제 변수를 선언 전에 실수로 사용할 위험이 사라짐.


해결책 2: 블록 스코프 지원 → {} 안에서만 유효함

if (true) {
  let y = 200;
}
console.log(y); // ❌ ReferenceError (블록 밖에서는 접근 불가능!)
  • 블록 {} 내부에서 선언된 변수는 블록을 벗어나면 사라짐 → 전역 오염 방지!

  • const도 동일한 블록 스코프를 가짐.


해결책 3: 중복 선언 방지

let z = 30;
let z = 40; // ❌ SyntaxError: Identifier 'z' has already been declared
  • 같은 변수 이름을 여러 번 선언할 수 없도록 강제 → 실수 방지!

  • const도 동일하게 중복 선언 불가.

👉 TDZ, 블록 스코프, 중복 선언 방지 기능을 추가해 더 안전한 코드 작성이 가능해졌다!

📌 ES6 이후 LexicalEnvironment의 역할

ES6에서 let, const, class 같은 블록 스코프 변수가 등장하면서, LexicalEnvironment는 let, const, class와 함께 동적으로 변경되는 블록 스코프 관리 역할까지 맡게 됨.

  • ES6에서도 실행 컨텍스트 생성 시점에는 두 환경이 동일한 EnvironmentRecord를 참조한다.

  • 실행 과정에서 let/const 선언이나 블록 스코프 진입 시 LexicalEnvironment가 새로운 환경으로 교체될 수 있다.

  • VariableEnvironment는 계속 원래의 환경을 유지한다.


📌 VariableEnvironment와 LexicalEnvironment를 구분하는 이유

1️⃣ varlet/const의 초기화 방식이 다름

var는 호이스팅되고 undefined로 초기화됨, let/const는 호이스팅은 되지만 초기화가 되지않고 TDZ 적용됨

console.log(a); // undefined (var는 호이스팅됨)
console.log(b); // ReferenceError (let은 TDZ 상태)
console.log(c); // ReferenceError (const도 TDZ 상태)

var a = 1;
let b = 2;
const c = 3;
  • letconst는 TDZ를 적용하기 위해 VariableEnvironment에 포함되지 않고, LexicalEnvironment에서만 관리됨.

2️⃣ let, const의 블록 스코프 관리

function example() {
  var a = 10;
  let b = 20;

  if (true) {
    var c = 30;
    let d = 40;
    console.log(a, b, c, d); // 10 20 30 40
  }

  console.log(a, b, c); // 10 20 30
  console.log(d); // ReferenceError (d는 블록 내부에서만 존재)
}

example();
  • var avar cVariableEnvironment에 저장됨 → 함수 스코프를 따름.

  • let blet dLexicalEnvironment에 저장됨 → 블록 스코프를 따름.


📌결론

VariableEnvironmentLexicalEnvironment의 구분은 JavaScript의 변수 선언 방식과 스코프 관리의 차이에서 비롯됩니다. ES6 이후, letconst의 도입으로 블록 스코프와 TDZ 개념이 추가되면서, 두 환경의 역할이 더욱 명확해졌습니다.

1️⃣ 초기화 방식의 차이

  • var는 즉시 초기화 (undefined 할당됨).

  • let/const는 TDZ 적용 (ReferenceError 발생).
    VariableEnvironment가 var를 즉시 초기화하도록 관리.
    LexicalEnvironment가 let, const의 TDZ를 적용하도록 관리.

2️⃣ 블록 스코프(let, const)와 함수 스코프(var)의 차이

  • varVariableEnvironment에서 함수 스코프를 가짐.

  • let, constLexicalEnvironment에서 블록 스코프를 가짐.
    LexicalEnvironment를 따로 관리하지 않았다면, let, const도 함수 스코프를 따라야 했음.