기록

JavaScript Engine - 2(호출 스택, 스코프, TDZ, 선언, 초기화...) 본문

JS/etc

JavaScript Engine - 2(호출 스택, 스코프, TDZ, 선언, 초기화...)

mnmhbbb 2022. 3. 22. 11:29

호출과 선언을 구분하라

 

호출스택

  • 참고로 console.log()는 다음과 같은 코드가 있다고 생각하면 됨
const console = {
  log() {
    // 콘솔에 글자 적는 기능
  },
};
  • 호출스택: 함수의 호출과 동시에 스택에 쌓임
  • 스코프체인: 함수의 선언으로 생기는 것. 블록 안에서 어떠한 값에 접근이 가능한지/불가능한지
const x = 'x';

function c() {
  const y = 'y';
  console.log('c');
}

function a() {
  const x = 'x';
  console.log('a');

  function b() {
    const z = 'z';
    console.log('b');
    c();
  }
  b();
}

a(); // a b c
c(); // c

// a b c c

 

여기서 헷갈릴 수 있는 부분:
함수가 호출되면 선언한 { } 부분이 실행되는 것

더보기

a(); 가 호출되고 다음 줄로 넘어가서 c(); 인데
그러면 c 스택이 쌓이는 것이 아니라,
a(); 호출됨과 동시에 스택에 a가 쌓이고, a를 선언하는 곳으로 가서 { } 부분을 실행함
함수가 호출되면 { } 부분이 실행되는 것!!! 당연함.
a 함수 안에 log(), b()가 있으므로 호출스택에 또 쌓임
} 까지 끝나면 콜스택에서 해당 스택이 빠져나옴
빠져나오면 } 가 끝난 부분으로 가는 것임. 
이 함수를 호출한 곳으로 다시 돌아감. 당연함.

 

호출스택을 보는 방법: debugger;

더보기

원하는 위치에 debugger; 를 추가
자바스크립트 엔진이 debugger를 만나는 순간, Sources 패널로 이동해서 우측에 Call Stack이 보임
여기에서 특정 스택을 클릭하면, 해당 함수에서 접근할 수 있는 스코프를 확인할 수 있음(스코프체인)

+) 호출스택을 보면 하단에 (anonymous) 부터 시작하는 것을 알 수 있음
쉽게 생각해서, 코드 전체를 실행하는 함수를 anonymous라고 보면 됨
그러면 맨처음에 anonymous라는 함수가 호출되고, 우리가 입력한 코드가 순차적으로 실행됨

 

 

스코프체인 파악을 위한 함수 선언 관계 정리

  // 다음과 같이 함수 b()의 선언 위치를 바꾸면 에러 발생: b is not defined
  const x = 'x';
  
  function c() {
    const y = 'y';
    console.log('c');

    // 함수 b를 이곳에서 선언하는 것으로 변경...!
    function b() {
      const z = 'z';
      console.log('b');
      c();
    }
  }
  
  function a() {
    const x = 'x';
    console.log('a');
    
    // 원래 이곳에 있던 함수 b를 함수 c 안에 선언하고, 이곳에서 함수 b를 호출한다면?
    // function b() {
    //   const z = 'z';
    //  console.log('b');
    //  c();
    // }
    b();
  }
  
  a();
  c();
  • 나를 포함하고 있는 함수를 찾아서 찾아서 anonymous까지 가기.(이때 호출은 신경쓰지말기)
    • 선언된 함수에서 위로 거슬러 올라가기(들여쓰기!)
  • 한 번 선언하면 스코프 관계는 절대 바뀌지 않음 (= 렉시컬 스코프)
  • 최상위에 c, a 함수 선언이 있음
  • A -> B : 다음에서 사용할 화살표는 함수 A를 포함하고 있는 함수 B를 의미함
  // 함수 선언에 따른 스코프 정리:
  c -> anony
  a -> anony
  b -> c -> anony
  • a 함수 선언 기준으로 b 함수에 접근할 수 없다. -> 따라서 not defined
  • 선언에 대한 정리: (실행컨텍스트 정리)
// 모든 선언에 대한 정리(실행 컨텍스트)
// 선언 맵 그리기
anony -> x, c, a 
c -> y, b 
b -> z 
a -> x
  • anony와 a에는 b가 없어서 접근할 수 없다. -> 따라서 not defined
  • 만약 anony, a 둘다 x라는 값이 있다면 어디에 선언된 x를 참조해야 할까?
  • a -> anonymous이 관계를 생각해서, a에서 x를 찾으면 거기서 종료임. anony에서 찾을 필요가 없음
  • 스코프체인은 a에도 없을 경우에 anony까지 거슬러 올라가는 것.
  • 그래서 선언은 웬만하면 상단에 쓰고, 선언하기 전에 접근하지 말자.(TDZ 문제)
  • 이러한 호이스팅이 생길만한 상황을 만들지 않는 것이 좋은 코드이다.
  • 왜냐하면 이 문제로 인해 자꾸 예외상황을 만들기 때문.
  • eslint에 호이스팅 사용하지 않는 룰을 적용해놓으면 조금 더 주의해서 작성할 수 있음
  • annoy -> 여기에서는 호이스팅 되는 것을 가장 먼저 그리고, 위에서부터 하나씩 그릴 것.
    • annoy 에서 모든 선언을 그리기 전에, 함수 호출이 있을 경우 콜 스택에 넣어서 그 함수를 실행할 수도 있기 때문.
    • 이 부분은 https://mnmhbbb.tistory.com/520 이 코드를 참조

 

호이스팅에 대한 추가설명

  var y;
  y = 'hehe'; // window.y = 'hehe';
  var y = 'test';
  • var은 호이스팅 될 때 var y = 'hehe'; 선언을 통째로 올리는 게 아니라
  • var y; 선언 부분만 올라감(값을 할당한 내용은 올라가지 않는다)
  • 심지어 var은 여러번 선언해도 됨
  • 따라서 var은 코드 분석 때 헷갈리게 만들고, y = 'hehe'; 처럼 직관적이지 않음
  • 추가로 function 함수선언식도 호이스팅되긴 하지만 통째로 되기 때문에 그리 헷갈리진 않는다.
  • 가능하면 화살표함수 쓰면 좋지만 어쩔 수 없는 경우가 있기 때문.
  • const 의 TDZ(Temporal Dead Zone)

드래그 부분이 const의 TDZ 구간이다.

  function a() {
    console.log(z);
  }

  const z = 'z1';
  a(); // z1
  • const 전까지는 TDZ인데 함수 a 안에서 z를 호출하는 것? 가능하다.
  • TDZ가 함수 내부까지 영향을 미치는 것은 아니기 때문에 다음과 같은 코드도 문제없다.
  • 정확히 말하면, 같은 스코프 안에서 const 이전 구간이 TDZ이다.
  • console.log(z)는 a의 스코프이기 때문에 상관이 없는 것!
  • 선언에 대한 정리를 다시 해보면,
    • annoy -> a, z
    • a -> console.log()

 

출처: https://youtu.be/0yJsvly3nuk

 

 

+)

TDZ

  • 일시적 사각지대
  • 초기화 전에 변수에 접근하는 것을 금지한다.
  • 스코프의 시작 지점부터 ~ 초기화 시작 지점까지의 구간
    • = 변수의 선언 ~ 변수의 초기화가 이루어지기 전까지의 구간
  • 아직 메모리가 할당되지 않았기 때문에(=아직 초기화되지 않았기 때문에) 값을 참조하려고 하면 에러가 발생한다.
  • let, const가 TDZ의 영향을 받음.
  • TDZ에 있는 변수 객체란, 선언은 되어있지만 아직 초기화되지 않아서 변수에 담길 값을 위한 공간이 메모리에 할당되지 않은 상태
  • 그래서 let, const의 초기화 단계에 오면 TDZ 상태가 해제됨

 

변수 선언

  • 선언 단계(Declaration phase) : 변수를 실행 컨텍스트의 변수 객체에 등록하는 단계를 의미합니다.
    이 변수 객체는 스코프가 참조하는 대상이 됩니다.
  • 초기화 단계(Initialization phase) : 실행 컨텍스트에 존재하는 변수 객체에 선언 단계의 변수를 위한 메모리를 만드는 단계(공간 확보)입니다. 이 단계에서 할당된 메모리에는 undefined로 초기화 됩니다.
  • 할당 단계(Assignment phase) : 사용자가 undefined로 초기화된 메모리의 다른 값을 할당하는 단계 입니다. 

 

출처: https://noogoonaa.tistory.com/78

 

TDZ(Temporal Dead Zone)이란?

함께보면 좋은 글 2020/07/05 - [프로그래밍 언어/Javascript] - 자바스크립트 호이스팅(Hoisting)이란? 오늘은 TDZ(Temporal Dead Zone)에 대해서 알아보도록 하겠습니다. 이번 포스팅은 자바스크립트의 호이스.

noogoonaa.tistory.com

https://ingg.dev/hoisting/

 

[JS] 호이스팅과 TDZ 이해하기

js 호이스팅(Hoisting)이란 변수나 함수를 선언했을 때 코드 범위(scope) 내의 최상단으로 끌어올려지는 것처럼 보이는 현상이다. 흔히 var 는 호이스팅이 발생하며, const 와 let 은 발생하지 않는다고

ingg.dev

 

  • var: 선언+초기화
  • let: 선언 -> 초기화
  • const: 선언+초기화+할당

 

호이스팅: 실제 코드의 위치는 변하지 않지만 브라우저가 JavaScript를 해석할 때(JS 엔진이) 변수와 함수의 선언을 스코프의 최상단으로 끌어올리는 것

'JS > etc' 카테고리의 다른 글

JavaScript Engine - 5. 비동기 시작(callback, promise)  (0) 2022.03.23
JavaScript Engine - 3. this  (0) 2022.03.23
JavaScript Engine - 1  (0) 2022.03.21
[복습] 자바스크립트 동작 원리  (0) 2022.03.17
import export { }  (0) 2021.03.11
Comments