20.10.28
[TIL] 변수와 스코프에 대해서 간단하게 정리하고 호이스팅과 TDZ(일시적 사각지대)에 대해서 알아보자.
아래의 코드를 보고,
function showA () {
console.log(a) //console에 어떤 값이 찍힐까?
var a;
a=1;
console.log(a)//console에 어떤 값이 찍힐까?
}
showA()
바로 답이 나오지 않거나, 답을 알 것 같아도 정확한 이유를 모른다면 아래의 글들을 보고 정리하면 좋을 것 같다.
변수란 말 그대로 "변하는 수"입니다. 그렇다면 무엇이 변하는가?
변하는 것은 "데이터" 입니다. 데이터라 하면 사이트에서 사용되는 유저정보, 사진, 게시물, 댓글 등 모든 것이 데이터 입니다. 이런 데이터들을 처리하기위해서는 데이터를 저장하는 공간이 필요합니다.
그 공간이 바로 메모리입니다. (흔히 RAM이라고 한다...)
변수는 바로 그 공간에 저장되는 것 입니다.
변수에도 여러종류가 있다. 이를 "데이터타입"과 "자료형"으로 분류하고 있다.
최신 ECMAScript 표준은 다음과 같은 자료형을 정리한다.
기본형(primitive type) - 여섯가지 데이터타입
참조형(reference type)
⭐️기본형은 값이 담긴 주소값을 바로 복제하는 반면, 참조형은 값이 담긴 주소값들로 이루어진 묶음을 가리키는 주소값을 복제한다.
: 변수의 라이프사이클(?), 변수가 생성되어서 소멸되기 까지의 범위
만일 만일 변수가 모든 함수에 속하지 않고 {}괄호안에 들어있지도 않다면 우리는 그 변수를 전역 변수라 합니다.
일단 전역 변수를 선언하면, 자바스크립트 코드 어디에서든 불러 쓸 수 있습니다. 심지어 함수 내부에서도 불러올 수 있다.
전역스코프에서 전역 변수를 선언할 수 있더라도, 전역 변수는 선언을 하지 않는 것이 권장됩니다. 왜냐하면 두개 혹은 그 이상의 변수들이 같은 이름을 가지게 되어 네이밍 충돌(naming collisions)이 발생할 확률이 있기 때문입니다.
코드 내 특정 구역( {} )에서만 사용할 수 있는 변수를 지역변수라고 하고 이 변수를 지정하는 범위를 지역스코프라고 한다.
지역스코프에는 두 종류의 스코프가 있다.
:모든 코드 블록(함수, if 문, for 문, while 문, try/catch 문 등) 내에서 선언된 변수는 코드 블록 내에서만 유효하며 코드 블록 외부에서는 참조할 수 없다. 즉, 코드 블록(중괄호) 내부에서 선언한 변수는 지역 변수이다.
: let 과 const 에 대해서만 동작한다.
:기존 var의 경우 함수 스코프를 가졌기 때문에 함수 내에서만 지역변수가 유지되는 문제가 있었다. ES2015(ES6)에서 let / const 키워드가 추가되면서 함수가 아닌 일반 블록에서도 지역변수를 선언할 수 있게 되었다.
: 함수 내에서 선언된 변수는 함수 내에서만 유효하며 함수 외부에서는 참조할 수 없다. 즉, 함수 내부에서 선언한 변수는 지역 변수이며 함수 외부에서 선언한 변수는 모두 전역 변수이다.
✔︎ es5에서는 함수에 의해서만 유효범위가 생길 수 있었다.
함수스코프 내의 this는 윈도우 객체 내를 가르킨다.
var로 선언한 변수는 블록스코프의 영향을 받지않는다.
이를 해결하기 위해서 let, const키워드를 es6에서 도입하였다.
중복선언 | 재할당 | |
---|---|---|
var | o | o |
let | o | x |
const | x | x |
✔︎ 문, 식, 값
~문(문단)
: if 문, for 문, switch-case문 : '문단'
: 결과를 저장하지 못한다. ex) if문이 끝나고 나도 그 결과를 어딘가에 담아둘 수 없다.
//if문
if(true){
} // 실행하고 끝. 다음으로 넘어감.
//for문
for (let i = 0; i< 10; i++){
}
=> 문 자체가 하나의 block-scope 가 된다.
~식
expression. 값이 될 수 있는 것(경우).
~값
'~식'과 동일하게 본다.
먼저 변수의 선언과 데이터 할당에 대해서 코드를 통해 보자면,
var a; //변수 선언 + 초기화
a=1; // 할당
var a=1; // 변수 선언, 초기화, 할당
위와 같다.
먼저, 위 코드의 첫번째 줄과 두번째 줄에 대한 내용을 해석(?)해보자면,
⇾ 변수를 선언하면 컴퓨터에서 비어있는 공간을 확보하고 이 공간의 이름을 "a"라고 지정한다. 그 다음 변수에 데이터를 할당하게 되면 데이터를 저장하기 위한 메모리를 확보한 후에 "1" 라는 값을 저장하고, "a"는 해당 메모리의 주소를 참조하게 된다. 세번째 줄은 위의 과정을 한번에 진행한다.
만약, 2 이라는 데이터로 재할당 한다면, 2이라는 데이터가 있는지 체크한 후 없으면 메모리 공간을 확보해 2을 저장할 것이다. 그리고 "a" 는 새로 만들어진 메모리의 주소(2가 저장되어있는 주소)를 참조하게 될 것이다.
1. 선언 단계(Declaration phase)
변수를 실행 컨텍스트의 변수 객체(Variable Object)에 등록한다. 이 변수 객체는 스코프가 참조하는 대상이 된다.
2. 초기화 단계(Initialization phase)
변수 객체(Variable Object)에 등록된 변수를 위한 공간을 메모리에 확보한다. 이 단계에서 변수는 undefined로 초기화된다.
3. 할당 단계(Assignment phase)
undefined로 초기화된 변수에 실제 값을 할당한다.
var 키워드는 선언 단계와 초기화 단계가 함께 이루어지고
(변수명을 최상단으로 끌어올리고 초기화를 실행한다.(undifined)),
const/let 키워드는 선언 단계와 초기화 단계가 분리되어 이루어진다.
const/let 은 스코프 최상단에서 선언단계는 실행되지만 (=호이스팅), 초기화는 변수선언문에 도달했을때 실행된다. (할당단계는 변수 할당문에 도달했을때)
=> 그러므로 초기화 이전에 변수에 접근하면 Reference Error 가 발생한다.
이를 "TDZ(일시적 사각지대)에 빠진다" 라고 한다.
아래에 예시!! ⬇︎
//if문 - 1
if (true) {
let a = 10
if (true) {
console.log(a) // reference Error: a is not defined
const a = 20
}
}
//if문 - 2
if (true) {
let a = 10
if (true) {
//상위스코프의 a를 출력한다.
console.log(a) // 10
}
}
위의 예제를 보면 if문의 console.log(a) 에서 reference Error 가 출력된다.
만약 const가 호이스팅이 되지 않았다면 const 키워드에 의해 생성된 a에 대해서 몰랐을 것이고, 상위 스코프의 let 키워드에 의해서 생성된 a를 아래의 코드와 같이 출력하였을것이다.
따라서 let, const는 스코프가 시작되는 순간에 호이스팅이 된다!
var 키워드로 선언된 변수는 선언 단계와 초기화 단계가 한번에 이루어진다. 즉, 스코프에 변수를 등록(선언 단계)하고 메모리에 변수를 위한 공간을 확보한 후, undefined로 초기화(초기화 단계)한다. 따라서 변수 선언문 이전에 변수에 접근하여도 스코프에 변수가 존재하기 때문에 에러가 발생하지 않는다. 다만 undefined를 반환한다. 이후 변수 할당문에 도달하면 비로소 값이 할당된다.
let 키워드로 선언된 변수의 생명 주기let 키워드로 선언된 변수는 선언 단계와 초기화 단계가 분리되어 진행된다. 즉, 스코프에 변수를 등록(선언단계)하지만 초기화 단계는 변수 선언문에 도달했을 때 이루어진다. 초기화 이전에 변수에 접근하려고 하면 참조 에러(ReferenceError)가 발생한다. 이는 변수가 아직 초기화되지 않았기 때문이다. 다시 말하면 변수를 위한 메모리 공간이 아직 확보되지 않았기 때문이다. 따라서 스코프의 시작 지점부터 초기화 시작 지점까지는 변수를 참조할 수 없다. 스코프의 시작 지점부터 초기화 시작 지점까지의 구간을 ‘일시적 사각지대(Temporal Dead Zone; TDZ)’ 라고 부른다.
참조 :
https://www.zerocho.com/category/JavaScript/post/57271d6e5aec14515b949b4b