렉시컬 환경 (Lexical Environment)

장세진·2023년 6월 16일
1

JavaScript

목록 보기
2/12
post-thumbnail

이 글은 kwangsunny님의 tistory를 참고해서 작성 한 것입니다.

오늘은 클로저를 공부할 때 또는 this를 공부할 때 자주 마주치던 단어 '렉시컬 환경(어휘적 환경)'에 대해서 정리를 해보자 한다.

렉시컬 환경이란 무엇일까...

렉시컬 환경

  1. 스크립트 전체
  2. 코드블록 {...}
  3. 호출된 함수
    가 가지고 있는 이론상 객체이다. 스크립트 전체, 코드블록, 호출된 함수는 모두 본인의 렉시컬 환경을 가지며 렉시컬 환경이 미치는 범위인 렉시컬 범위를 가진다. 우리가 흔히 얘기하는 스코프에 대한 개념이 여기에서 나오는거 같다.

렉시컬 환경의 구성요소

  1. 환경 레코즈
  2. 외부 렉시컬 환경
    으로 구성된다. 환경레코즈는 현재 실행중인 코드 환경의 this값과 선언된 모든 변수와 함수가 저장되는 곳이라고 하는데 함수의 실행블록 또는 코드블록에 있는 모든 데이터를 의미하며 외부 렉시컬 환경은 렉시컬 환경의 부모 렉시컬 환경이다. 부모 렉시컬 환경 또한 환경 레코즈와 외부 렉시컬 환경을 가지며 root에 있는 전역 렉시컬 환경은 외부 렉시컬 환경으로 null값을 가진다.

아래 코드를 통해 렉시컬 환경을 이해해 보자.

<script>
    console.log(ex1) // Cannot access 'ex1' before initialization
	console.log(ex2) // Cannot access 'ex2' before initialization
	console.log(ex3) // undefined

    const ex1 = 'const';
  	let ex2 = 'let'
    var ex3 = 'var';

    function f(){
        const name = 'sejin'
        console.log(`${name} ${ex1} ${ex2} ${ex3}`); // 'sejin const let var'
    }
    
    f();
</script>

코드에는 몇개의 렉시컬 환경이 존재할까... 첫째 스크립트가 가지는 전역 렉시컬 환경이 있으며 두번째로는 함수 f가 가지는 렉시컬 환경이 있다. 만약 if문을 사용했다면 코드블록이 가지는 렉시컬 환경이 추가 되었을 것이다.

렉시컬 환경의 작동원리를 바탕으로 나머지 코드에 대한 이해도 해보고자 한다.

작동원리

실행중인 함수 내에서 어떤 변수가 나타나면 제일먼저 자신의 렉시컬 환경의 환경 레코드를 찾는다. 환경 레코드 안에 변수가 존재하지 않는다면, 외부 렉시컬 환경의 참조값을 통해 외부의 렉시컬 환경에서 해당 변수를 찾게되고 이 과정을 변수를 찾을때까지 반복한다. 만약 가장 바깥쪽 렉시컬 환경 (Global Lexical Environment) 에서도 이 변수를 찾지 못하면 Reference 에러가 발생한다.

함수 f는 렉시컬 환경에 저장된 변수 name을 참조, 이후 ex1, ex2, ex3 변수는 외부 렉시컬 환경의 변수를 참조한다.

렉시컬 환경이 만들어질때 모든 변수와 함수가 환경 레코드에 저장된다고 했다. 그래서 자바스크립트 엔진은 const ex1, let ex2 의 존재를 알고는 있다. 하지만, const과 let은 변수가 선언되기 이전엔 uninitialized 라는 상태를 가지고 있어서 이때 접근하면 에러가 발생하게되고 변수가 선언된 이후부터 접근이 가능하다. 반면에 var로 선언된 변수는 렉시컬 환경에 올라가자마자 undefined로 초기화가 된다. 그래서 ex3는 에러가 나지 않고 undefined 가 출력되는 것이다.

변수의 존재를 알고 있다는 것의 이해를 위해서 아래 코드를 보면

<script>
    const ex4 = 'a'
	var ex5 = 'b'

    function f () {
        console.log(ex4) // Cannot access 'ex4' before initialization - (A)
      	console.log(ex5) // undefined - (B)
        const ex4 = 'c';
      	var ex5 = 'd';
        console.log(ex4) // 'c'
      	console.log(ex5) // 'd'
    }

    f()
</script>

얼핏 보면 (A)는 'a', (B)는 'b'가 콘솔로 출력될 것 같지만 그렇지 않다.

왜나하면 함수 f는 자신의 렉시컬 환경의 환경 레코드를 먼저 찾은 이후 해당하는 데이터가 없을 때 외부 환경변수를 찾기 시작하기 때문이다. 함수 f는 f의 환경 레코드에 선언된 ex4와 ex5의 존재를 알고 있다. 따라서 (A)와 (B)는 ex4의 초기 상태 uninitialized, ex5의 초기화 값 undefined를 출력한다.

추가개념

  1. 외부 렉시컬 환경은 함수가 실행되는 시점이 아닌 선언된 시점의 외부 환경을 가리킨다.

아래 코드에서 함수 f2의 외부 렉시컬 환경은 f1 렉시컬 환경이 아닌 전역 렉시컬 환경이다. 따라서 'jang'을 출력한다.

<script>
    let a = 'jang';

    function f1() {
      let a = 'sejin';
      f2();
    }

    function f2() {
      console.log(a); // 'jang'
    }

    f1();
</script>
  1. 함수 선언문으로 정의된 함수는 렉시컬 환경에 생성되자마자 메모리에 올라가기 때문에 바로 사용할 수 있다.

위와 같은 이유로 호이스팅이 발생한다. 호이스팅은 코드가 렉시컬 환경의 최상부로 이동하는 것이 아닌 메모리에 생성과 동시에 메모리에 등록되기 때문에 발생한다.

profile
풀스택이 되고 싶은 프론트엔드 개발자

0개의 댓글