[JS] 자바스크립트의 실행 컨텍스트

baegyeong·2024년 7월 7일

Java Script

목록 보기
3/9
post-thumbnail

코어자바스크립트 ch2. 실행컨텍스트를 읽고 제 생각과 함께 정리한 내용입니다.


왜 VariableEnvironment가 필요할까?

어차피 LexicalEnvironment에 똑같이 복사되고, 변경사항까지 반영하는데…

실행 컨텍스트를 생성할 때 VariableEnvironment에 정보를 먼저 담은 다음, 이를 그대로 복사해서 LexicalEnvironment를 만들고, 이후에는 LexicalEnvironment를 주로 활용하게 됩니다. (p.40)

⇒ LexicalEnvironment를 주로 활용하게 된다는데, 그렇다면 VariableEnvironment는 단지 복사를 위한 것인가?

VariableEnvironment와 LexicalEnvironment의 차이

임시스코프와 블록스코프가 있는 경우에 차이가 생긴다.

  • 임시스코프가 생기는 경우
    • with문(지원하지 않음)
    • catch문 사용(예외처리)
  • 블록 스코프
    • let, const

위와 같은 상황에서는 별도의 실행컨텍스트를 생성하지 않고 임시의 LexicalEnvironment만 생성한다. (VariableEnvironment에 영향 x → 스냅샷이며 변경사항은 저장안하니까!!)

실행한 scope가 종료되고 다시 원래 상태로 복구 할 때, VariableEnvironment가 필요하다.


VariableEnvironment는 원래의 스코프의 정보를 유지한다. 왜 유지해야하며, 그럼 LexicalEnvironment는 유지하지 않는 것인가?

원래의 스코프 정보의 유지가 필요한 이유

var로 선언된 변수의 호이스팅 동작을 구현하기 위해 원래의 스코프 정보가 필요하다.

블록스코프, 임시스코프가 임의의 LexicalEnvironment만을 생성해서 실행하고 다시 복구할 때, 원본 정보인 VariableEnvironment가 필요하다.

LexicalEnvironment도 스코프 정보를 유지한다.

임시스코프가 생성되면 LexicalEnvironment가 변경된다.

현재 스코프 체인을 반영한다.


LexicalEnvironment는 VariableEnvironment를 깊은 복사할까?

위 주제에서도 인용한 부분이지만, VariableEnvironment을 그대로 복사해서 LexicalEnvironment를 만든다고 했다. 그럼 깊은 복사를 하는걸까?

⇒ 복사하지 않고 참조를 하는 것이다.

claude가 v8코드를 단순화했다. (실제코드는…..알아보기가 힘들었다……)

Handle<Context> Factory::NewContext(Isolate* isolate, Handle<ScopeInfo> scope_info) {
  // ... (기타 코드)
  
  Handle<Context> context = Handle<Context>::cast(
      NewJSObjectFromMap(map, AllocationType::kOld));
  
  context->set_scope_info(*scope_info);
  context->set_previous(*isolate->native_context());
  
  // LexicalEnvironment와 VariableEnvironment를 동일하게 설정
  context->set_extension(*the_hole_value());
  context->set_variable_object(*context);
  
  // ... (기타 코드)
  
  return context;
}
  • Context 객체는 자바스크립트의 실행 컨텍스트이다.
  • set_extension, set_variable_object
    • LexicalEnvironment와 VariableEnvironment를 참조를 통해 동일시

화살표 함수의 실행 컨텍스트

함수 선언문은 전체를 호이스팅하고, 함수표현식은 변수 선언부만 호이스팅한다.

화살표 함수는 어떨까?

⇒ 함수 표현식과 같다고 할 수 있다.

우선 화살표 함수는 함수 표현식에 대한 간결한 대안이다. (mdn)

console.log(sum(1,2));
console.log(multiply(3,4));

function sum(a,b){ // 함수 선언문
  return a+b;
}

var multiply = (a,b) =>{ // 화살표 함수
  return a*b;
}
var sum = function sum (a, b) {
	return a + b;
};

var multiply;

console.log(sum(1,2));
console.log(multiply(3,4));

multiply = (a, b) =>{
	return a*b;
}

책에 나온 예제에서, multiply를 함수 표현식으로 선언한다면 multiply는 ReferenceError가 발생한다.

마찬가지로 multiply를 화살표 함수로 표현하면, 동일하게 ReferenceError가 발생한다.


let과 const는 호이스팅이 안될까?

console.log(a) // undefined
var a = 3;
console.log(a); // ReferenceError
const a = 3;

왼쪽은 선언된 변수가 호이스팅 되서 undefined가 출력됐고, 오른쪽은 a를 찾지 못하고 ReferenceError가 발생했다.

그렇다면 let과 const는 호이스팅이 안되는걸까?

TDZ는 스코프의 시작 지점부터 초기화 시작 지점까지의 구간을 말한다.

  • 변수 라이프 사이클에서, var은 변수 선언과 초기화가 한번에 일어난다.

    • 따라서 왼쪽의 코드에서 undefined가 호출된다
    • 내가 초기화 안해도 undefined로 초기화까지 해준다.
  • 참고) 함수 선언문은 선언, 초기화, 할당이 동시에 일어난다.

  • let과 const는 선언단계와 초기화 단계가 분리되어있다.

    • let과 const는 호이스팅이 되나, var처럼 초기화까지 되는 것은 아니다. → 초기화 전까진 접근이 안되니 호이스팅이 안되는 것처럼 보인다.

    • undefined로 초기화된 것을 리턴하는 대신 ReferenceError를 반환한다 → 더 예측 가능하게 한다.

      💡 ECMAScript 2015 spec.
      NOTE let and const declarations define variables that are scoped to the running execution context’s LexicalEnvironment. The variables are created when their containing Lexical Environment is instantiated but may not be accessed in any way until the variable’s LexicalBinding is evaluated. A variable defined by a LexicalBinding with an Initializer is assigned the value of its Initializer’s AssignmentExpression when the LexicalBinding is evaluated, not when the variable is created. If a LexicalBinding in a let declaration does not have an Initializer the variable is assigned the value undefined when the LexicalBinding is evaluated.

      let과 const는 LexicalEnvironment에 변수가 저장되지만, LexicalBinding을 거쳐야 접근이 가능하다.

      LexicalBinding은 특정 값으로 초기화되며 그 값이 없다면 undefined가 할당된다.



참고자료

how-js-works-lexical-environment

Temporal Dead Zone

0개의 댓글