Record4me

시작하면 끝을 봐야지

TIL

[TIL] 24_0105 실행 컨텍스트, 호이스팅

잇츄미 2024. 1. 5. 20:59

js문법 3주차 강의보고 궁금했던 점 해결

배운 것

📌실행 컨텍스트 : 실행할 코드에 제공할 환경 정보들을 모아놓은 객체야

=> 그리고 이런 객체들은 '스택'의 한 종류인 '콜스택'에 쌓여.

*콜스택 : 가장 위에(마지막에) 쌓인 컨텍스트와 관련된 코드를 실행하는 방법인데,
한 코드의 객체가 실행이 다 되면 없어지는 방식이야. 

 

📌 호이스팅 : 자바스크립트의 특징 하나인데, 변수 선언부(var a = 1;이면 그 중 var a;가 변수 선언부)만을 최상단으로 올려버리는 거야. var, let, const 다 일어나지만

그에 따라 나오는 답이 달라.

var 같은 경우 최상단으로 선언부가 올라가면 값이 undefined나오고, let과 const는 호이스팅이 일어나긴 해 하지만 ES6의 규칙으로 TDZ라는 구간이 존재하기 때문에 에러를 내게 되서 일어나긴 하려던 호이스팅을 방지할 수 있게 되어있어.

 

*TDZ (Temperal Dead Zone : 일시적 사각 지대)

 

*ES6도 여전히 호이스팅이 일어나긴 하는 예의 참고 사이트
: https://poiemaweb.com/es6-block-scope (모던 자바스크립트 deep dive 저자분 블로그- 모던 자바스크립트 책과 대부분 거의 동일한 내용)

 


예를 들어 이런거지,

호이스팅

이렇게 콘솔로그와 키워드 var로 만든 변수가 있다면

console.log(a); //undefined
var a = 1;

콘솔로그를 실행했을 때 a의 값은 undefined가 나와.

콘솔로그로 값을 찍기 전에 값이 할당된 변수 a가 콘솔로그 위에 선언되지 않았기 때문이라고 기본적으로 볼 수 있고

무엇보다 호이스팅이 일어난 아래의 모습은 위와 같은데, (아래 문과 위의 문은 같은 형태라고 볼 수 있어 선언부와 값이 변수 할당부가 나뉜 거)

var a;
console.log(a); // undefined
a = 1;

위에 변수 a는 선언은 됐지만, 변수에 할당이 안 되서 콘솔로그로 a의 값을 찍었을 때 undefined로 값이 정의되지 않았다고 나오는 거야.

 

 

let과 const의 경우는 호이스팅이 되어도 ES6의 규칙으로 호이스팅이 되면 값이 할당되지 않은 값을 도출해내면 참조오류를 내게 해. 

예를 들어, 이 식은

console.log(a); //ReferenceError: Cannot access 'a' before initialization
let a = 1;

 

 

 

이렇게

let a;
console.log(a); // ReferenceError : ~ 대충 a를 읽을 수 없으니 위에 값이 할당도 안 된 거 값을 찍을 수 없다는 경고를 주라고 ES6에서 이렇게 정했어.
a = 1;

 

이렇게 되면 let도 호이스팅은 일어나지만 값이 할당되지 않은 변수를 찍게 된 거라 ES6의 규칙으로 참조 오류를 내게 돼.

참조오류(ReferenceError) : 초기화 전의 a를 읽을 수 없다라는 오류를 내줘서 경고를 주는 거지. 

 

=>이렇게,

 

 

실행 컨텍스트

저번 주말에 실행 컨텍스트 강의를 보고 zep의 사람들에게 의견을 물어봤어. 

허나 각자 다른 의견이 나와서 종합해서 같이 고민했어. 

 

이게 그 문제의 구조야. 

var a = 1;
function outer() {
  function inner() {
    console.log(a); // undefined
    var a = 3;
  }
  inner();
  console.log(a);
}
outer();
console.log(a);

 

 

이건 이런 형태로 콜스택에 쌓이는데(아래 그림, 못 그렸어도 ㄷ스킵)

 

실행 컨텍스트 쌓이는 순서

1. 우선 전역 실행 컨텍스트에 전역 스코프를 가지는 var a  = 1; 가 컨텍스트의 정보로 남고,

 

2. outer함수 선언문을 넘어서 outer()라는 함수가 호출되서 실행되니 outer 함수실행된다는 정보를 담은 컨텍스트가 쌓여,

 

3. 그다음 실행될 outer함수의 outer함수 선언문을 속을 읽어 내려가서 inner선언문을 지나면, outer()함수 내에서 inner()라는 함수가 호출되서 실행된다는 정보를 담은 컨텍스트가 쌓여.


그렇게 inner()함수의 함수 선언문이 읽어 내려가면 console.log(a)도 실행되는 애니까 컨텍스트로 쌓이고(console.log(a);도 실행 컨텍스트라 콜스택에 쌓이긴 하겠지만 굉장히 미비한 애라 빼고 그리는 겨.) var a = 3;이 컨텍스트에 정보로 남겠지. 

(하지만 실제 문서에서 쓸 때는 console.log()로 확인해야 좋지. 호이스팅같은 문제로 값을 실제로 찍는 타이밍 위치가 중요행. )

 

실행과 실행 컨텍스트 나가는 순서

1. 이렇게 쌓이고 실제로 동작하게 되면 순서대로 outer()함수가 호출되면서(호출만 되고 실행이 되는 중이지 실행이 끝난 게 아니라서 컨텍스트는 아직 그대로 남아있음)내부에 있는 inner()함수가 연달아 호출되고,
호출된 inner()함수 선언문을 읽어 내려가면서 console.log(a);가 실행되고 var a = 3;을 지나 inner()함수가 실행이 완료되면 inner컨텍스트는 콜스택에서 빠져나가서 사라지게 돼.

 

2. 그리고 outer()함수의 내부의 inner()함수가 실행이 끝나면 outer()함수 선언문의 내부에 아직 남은 실행되는 미비한 컨텍스트인 console.log(a);가 실행되면서 사라지고 outer()함수의 실행이 다 끝나. 그렇게 outer 실행 컨텍스트도 콜스택에서 빠져나가서 사라지게 돼. 그후 또 미비한 console.log(a); 컨텍스트가 실행이 되고 사라지면 전역 컨텍스트의 정보만 남게 되지.

 

그렇게 실행이 끝나면서 값이 이렇게 순서대로 뜨게 돼. 

 

 

여기서 드디어 문제의 고민,

inner()함수 내부의 console.log(a);의 결과가 왜 undefined이 왜 나왔냐는 거야. 

var a = 1;
function outer() {
  function inner() {
    var a; // 실제 코드에선 안 보이겠찌만 호이스팅이 이렇게 암묵적으로 일어나서 변수 선언부가 이렇게 최상단으로 올려져.
    console.log(a); // undefined
    a = 3; // <=== 호이스팅이 일어나면 여긴 이런 모습이지.
  }
  inner();
  console.log(a);
}
outer();
console.log(a);

 

 

사람들과 얘기하고 나온 의견들론,

1. console.log(a);를 찍기 전에 값을 할당한 변수를 초기화하지 않아서 이렇게 나온 것,

2. 왜 전역변수인 var a = 1;을 참조하지 않는 걸까? 라는 것,

3. 호이스팅으로 인해 var a;라는 값이 할당이 안 된 변수 선언부만이 최상단에 남아있는데, 값이 할당되지 않은 변수 이름을 참조하니 값이 정의되지 않았다며 undefined이라는 결과가 나왔다는 것. 

4. TDZ구간이 있어서이기 때문인 것.

 

확인해 본 결과, 1도 3도 맞는 얘기야(아래 문제해결 참고). 2같은 경우는 outer()함수는 바로 상위 스코프인 전역변수를 참조하지만 inner()함수는 지역 스코프가 되서 지역 변수만을 참조해서.

 

 

 

문제 해결

첫번째, 지역 스코프라 지역 변수를 참조해. 즉 inner함수에 console.log(a); 위로 값이 할당된 변수 a가 선언되면, console.log(a);는 그 변수의 값을 참조해서 해당 변수 값이 결과로 나오지.

두번째, undefined는 inner()함수 내부의 console.log(a); 값인데 호이스팅이 일어나면서, 값이 할당되지 않은 변수 이름을 참조해서 값을 찍으려고 하니, undefined라고 나오면서  a 값이 정의되지 않았다라고 나와.

 

 

느낀 점

- 이제