번역 - JavaScript 실행 컨텍스트 — lexical environment와 block scope (part 3)

자바스크립트 세부사항 스터디

Posted by jopemachine on September 29, 2022 Original Posted by Carson Updated on October 07, 2022

JavaScript 실행 컨텍스트 — lexical environment와 block scope (part 3)

JavaScript에는 ES6 업데이트 이후 세 가지 유형의 scope가 있습니다.

  • Global Scope

  • Function Scope

  • Block Scope

실행 컨텍스트 관점에서 scope란 무엇입니까?

Global scope는 전역 실행 컨텍스트, Function scope는 함수 실행 컨텍스트와 관련됩니다.

ES6에서 도입된 Block scope는 두 형제와 다릅니다.

Global scope의 예

Block scope를 이해하는 가장 쉬운 방법은 다른 두 scope와 비교해 보는 것입니다.

변수는 Global scope, Function scope에서 유사하게 작동하므로 이 게시물에서는 Global scopeBlock scope의 차이에 대해서만 설명합니다.

이 경우에는 하나의 Global 실행 컨텍스트와 하나의 Global variable environment만 있습니다.

두 번째 apple 할당문이 첫 번째 apple 할당문을 덮어 씁니다. 실행이 끝나면 “banana” 값이 저장된 apple 변수 하나만 남게 됩니다.

Scope 관점에서 보면, apple 변수가 Global scope에 있다고 말할 수 있습니다.

Block scope

위의 예를 let으로 다시 작성하여 새로운 범위인 Block scope를 생각해 볼 수 있습니다.

콘솔은 두 개의 다른 값을 기록합니다. 첫 번째 apple 변수는 값 “apple”을 보유하는 반면, if문 내부에서 이 값은 “banana”입니다.

어떻게 같은 이름을 가진 두 개의 변수가 가능할까요?

Lexical environment

2개의 프로세스를 통해 어떻게 작동하는지 알아보도록 하겠습니다.

컴파일 단계에서 undefinedapple 변수가 Global 실행 컨텍스트에 추가됩니다.

이 순간 JavaScript 엔진은 두 가지 이유로 두 번째 apple을 건너뛰기로 결정합니다.

  • 이 변수는 let으로 선언되었습니다.

  • 이 변수는 Block scope 내에 있습니다.

다음으로 실행 단계가 시작됩니다. 첫 번째 apple 변수에는 “apple” 값이 할당됩니다.

if문을 읽을 때 중첩 컴파일 단계가 발생합니다. undefined인 두 번째 apple 변수가 생성됩니다.

Variable environment에 생성하는 대신 이 apple 변수는 Lexical environment에 추가됩니다.

그 다음 “banana”라는 값이 Lexical environment에서 apple에 할당됩니다.

이제 두 환경에서 관리되는 동일한 이름을 가진 두 개의 변수가 있습니다. 이것이 JavaScript 엔진이 let을 처리하는 방식이며 여전히 var와 역 호환(backward compatible) 됩니다.

Lexical environment에서의 Scope 스택

letvar의 차이점을 더 잘 이해하기 위해 재미있는 예제에서 함께 결합해 보겠습니다.

컴파일 단계에서 undefined 상태인 applegrape 변수가 variable environment에서 초기화됩니다. grape 초기화는 호이스팅 됩니다. 한편, Lexical environment에서는 banana 변수가 생성됩니다.

실행 단계가 시작됩니다. apple에는 “global apple” 값이 할당되고 banana에는 “global banana”가 할당됩니다.

이제 Block의 변수를 처리할 때입니다.

그런데, 여기 또 다른 banana 변수가 있습니다. 동일한 Lexical environment에 두 개의 banana 변수를 가질 수 있습니까?

Block scope에서 letconst 변수를 봤을 때 JavaScript는 해당 변수에 대해 별도의 영역을 만듭니다. Lexical environment는 변수에 대해 스택과 같은 구조를 유지하므로 이름이 같은 변수는 서로 충돌하지 않습니다.

여기에서 undefinedbananaorange 변수는 서로 간섭하지 않는(stand-along) 범위에 있습니다.

그 다음 두 변수에 그에 상응하는 값이 할당됩니다.

마지막 할당문을 실행하면 모든 변수가 준비됩니다.

첫 번째 변수를 콘솔에 출력할 때 JavaScript 엔진은 먼저 Lexical environment에서 위에서 아래로 apple을 찾으려고 시도합니다. 그런 다음 Global variable environment를 검사하고 “global apple”를 출력할 apple을 찾습니다.

banana를 검색할 때 JavaScript 엔진은 동일한 단계를 따라 “block banana”를 출력합니다.

이 단계에서, 현재 블록에 더 이상 실행 가능한 코드가 남아 있지 않으므로, block scope가 제거됩니다.

자바스크립트가 계속 실행됩니다. Global variable environment에서 bananagrape를 찾아 “global banana”와 “global grape”를 출력합니다.

자바스크립트가 orange를 검색할 때 이 변수는 어디에도 존재하지 않습니다. orange가 존재했던 scope가 이미 제거되었기 때문이죠. “orange is not defined.” 라는 에러를 던질 것입니다.

전체 자바스크립트 실행이 끝났습니다.

Scope 외에 letconst와 관련해 뭔가 추가로 이해하신 점이 있으십니까?

변수 생성, 초기화, 할당에 관한 트릭

컴파일에서 실행까지 변수는 세 단계를 거칩니다.

  1. 생성

  2. 초기화

  3. 할당

콘솔은 무엇을 기록할까요? apple일까요? 또는 banana일까요?

놀랍게도, Cannot access apple before initialization. 라는 에러를 출력합니다.

이 오류는 호이스팅과 관련이 있습니다.

  • let 변수의 경우 생성은 호이스팅 되지만, 초기화, 할당 과정은 거치지 호이스팅 되지 않습니다.

  • var 변수의 경우 생성, 초기화는 호이스팅 되지만 할당은 호이스팅 되지 않습니다.

  • function의 경우, 생성, 초기화, 할당은 모두 동시에 호이스팅 됩니다.

사람들은 변수 초기화 이전의 코드 영역을 Temporal dead zone (TDZ) 이라고 명명했습니다.

  • 변수 생성 전 이 변수에 접근하려 하면, [variable name] is not defined.란 에러 메세지를 보게 됩니다.

  • 초기화 전 변수에 접근하려 하면, Cannot access [variable name] before initialization.란 에러를 보게 됩니다.

  • 할당 전 변수를 출력해보면 undefined 값을 보게 됩니다.

Scope와 Lexical environment에 대해 알아야 할 모든 것을 살펴보았습니다.

결론

  • Lexical environment는 실행 컨텍스트의 또 다른 구성 요소 입니다.

  • letconst 변수는 컴파일 단계 대신 실행 단계에 block scope에 만들어집니다.

  • 이 변수들은 Lexical environment에 저장됩니다.

  • 여러 block scope들은 스택 구조로 Lexical environment에 저장됩니다.

  • 자바스크립트 엔진이 block scope 내의 모든 코드들을 실행시키고 나면, 관련된 letconst 변수들은 모두 제거됩니다.

원문

해당 게시물은 원작자의 허락을 받고 번역되었습니다. 이 글의 모든 저작권은 원작자에게 있습니다.

This article is a translated version of below article. All rights goes back to him.