기록
JavaScript Engine - 2(호출 스택, 스코프, TDZ, 선언, 초기화...) 본문
호출과 선언을 구분하라
호출스택
- 참고로
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
[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 |