JS 변수 정의 과정 (hoisting)

강정우·2022년 10월 8일
0

JavaScript

목록 보기
18/53
post-thumbnail

JS 변수 정의 과정

  • JS engine은 JS 코드를 읽고 parsing 후 실행하는 프로그램이다.
  • 작성한 JS code는 JS engine을 통해 AST로 parsing 되고 실행된다.
  • Chrome 브라우저의 경우 V8 engine을 사용한다.

JS engine (Node.js)

  • node.js는 브라우저 외의 환경에서 JS코드를 실행하도록 하는 프로그램이다.
  • node.js는 여러 프로그램으로 구성되며, JS코드를 읽는 프로그램으로 V8을 사용한다.
  • 브라우저 환경과 node.js환경은 같은 JS코드를 작성해도 다르게 동작할 수 있다. 즉, 같은 코드도 환경에 따라 다르게 동작한다.

JS 코드의 실행

  • JS engine은 코드 실행 전 실행 컨텍스트를 생성한다.
  • 실행 컨텍스트는 두 단계(생성단계, 실행단계)를 통해 생성된다.
  • 생성 단계에서 JS engine은 변수 선언을 읽는다.
  • 실행 단계에서 JS engine은 변수 값을 할당한다.
  • 즉 변수에 값을 할당하는 것과 변수를 VO(variable object)에 선언하는 시점이 다르다!

렉시컬 환경(Lexical Environment)

  • 함수의 렉시컬 환경은, 함수가 사용하는 변수들을 둘러싼 환경을 의미한다.
  • 특정 변수의 값은 함수의 렉시컬 환경 안에서 찾을 수 있다.
  • 렉시컬 환경은 실행 컨텍스트 안에 정의된 Variable Object로 이해할 수 있다.
  • 즉 위 그림에 빗대어 설명을 하자면 우선 최초 함수가 실행된다면 EC 실행 context가 생성되고 안에 pointer, this, scope chain이 생성된다는 것을 앞서 포스팅 했었다. 하지만 여기에 추가적으로 LE가 생성되고 이는 VO의 집합체라고 생각해도 좋다. 그래서 만약 함수 내부에 없는 변수가 있다면 scope chain으로 모든 stack에 쌓인 EC를 돌아다니는 것이 아닌 EC에서 찾는다고 생각하면 된다.

생성 단계에서의 코드 실행

  • JS engine은 생성단계에서 함수 선언문, 함수 표현식, 변수 등을 읽어 실행 context에 저장한다.
  • 변수의 경우, 실행 context의 LE 환경을 구성한다.
  • 함수 선언문 외에 변수는 값이 저장되지 않는다. 즉, 함수는 값이 저장되지만 변수는 값이 저장되지 않는다. 이때 함수선언문은 함수명과 함수 전체를 뜻한다.

    실행 context를 생성

실행 단계에서의 코드 실행

  • JS engine은 변수에 값을 할당하는 구문을 만나면 EC에 값을 저장한다.
    즉, 그 전까지는 varundefined, let, constuninitialized 이었다가 해당 변수를 할당하는 문장을 만나면 그때서야 변수의 값을 할당한다.
  • 그 외 코드를 한 줄씩 읽어 나가면 실행한다.

    변수 값 할당, 코드 실행

JS Hoisting

코드 실행 시 변수 처리

  • JS engine이 코드를 읽으면, 생성단계에서 실행 context EC를 생성한다.
  • 이때 함수 선언문은 실행 단계에서 함수 전체가 실행 context에 저장된다.
  • var 변수는 저장 시 undefined로 초기화 된다. 즉, let, const와는 다르게 생성단계에서 선언이 된다.
  • let, const는 초기화되지 않는다.

Hoisting

var

colsole.log(callme())  // undefined

var x = 10

console.log(callme())  // 10

function callme(){ return x }
  • Hoisting은 변수가 선언된 시점보다 앞에서 사용되는 현상이다.
  • 이는 var변수가 생성단계에서 undefined로 초기화되는 것이 원인이다.
  • 함수는 생성 단계에서 함수 전체가 저장되므로 뒤에서 선언되어도 호출이 가능하다.

let, const

colsole.log(callme())  // ReferenceError : Cannot access 'a'

let x = 10

console.log(callme())  // 10

function callme(){ return x }
  • let, const 변수는 생성단계에서 초기화되지 않는다.
  • 선언문 이전에 접근 시 ReferenceError가 발생한다.
  • 이 경계를 Tempoal Dead Zone(TDZ)라 한다. 따라서 let, const는 hoisting이 발생하지 않는다.

정리

  • var, let, const 모두 변수를 선언하는 키워드
  • var, let은 변수에 재할당이 가능하지만, const는 재할당이 불가능하다.
  • var는 함수 스코프, let과 const는 블록 스코프 변수이다.
function varFor(){
	for (var i = 0; i < 3; ++i){
    	setTimeout(() => console.log("i :", i), 0);
    }
}

function letFor(){
	for (let i = 0; i < 3; ++i){
    	setTimeout(() => console.log("i :", i), 0);
    }
}

varFor();  // 3 3 3
letFor();  // 0 1 2
  • varFor에서 i는 varFor() 범위에 존재하는 변수이다.
  • 따라서 setTimeout이 호출될 때, i는 for 블럭이 끝난 시점에 소멸하지 않는다. 즉, 0,1,2가 지나고 3이 되었을 때 끝날 때를 함수가 완전히 종료될 때까지 유지하기 때문에 3만 계속하여 출력된다.
  • letFor에서 i는 for 블럭 안에 존재하는 변수이다.
  • 각 for block이 실행되고 i는 소멸한다. 다만, 이 경우 각 화살표 함수의 closure에 저장된다. 즉, 서로다른 3가지의 메모리에 참조되어서 정상적인 for문이 동작할 수 있다.
profile
智(지)! 德(덕)! 體(체)!

0개의 댓글