공부/Java script

[java script] 호이스팅 그리고 var,let,const의 차이점

<오늘부터1일> 2022. 4. 22. 13:07

-본 포스팅은 이웅모 저자님의 모던 자바스크립트 deep dive를 참조하였습니다.

 

1. 자바스크립트가 변수를 선언하는 방식

 

변수 = 하나의 값을 저장하기 위해 확보한 메모리 공간을 식별하기 위해 붙인 이름 
할당 = 변수에 값을 저장하는 것
참조 = 변수에 저장된 값을 읽어 들임

 

선언이란 변수를 생성하는 것을 말한다. 

값을 저장하기 위해 메모리 공간을 확보하고, 확보된 메모리 공간에 값을 저장할 수 있게 준비하는 것이다.

 

우리가 변수를 선언하면 js 엔진은 undefined 라는 값이 없는 상태로 초기화한다.

'선언'만 된 상태에서는 변수를 참조해보면 undefined가 읽어질 것이다.

 

 

 


🐣변수선언의 수행

 

선언단계 : 변수 이름을 등록해서 자바스크립트 엔진에 변수의 존재를 알림.
초기화단계 : 값을 저장하기 위해서 메모리 공간을 확보하고, 암묵적으로 undefined를 할당함.

 


 

 

var 키워드를 사용한 변수 선언은 위와 같은 단계가 동시에 진행된다.

 

2. 변수 호이스팅

 

다음과 같이 변수를 선언했다고 가정해보자.

 

1
2
3
console.log(foo); //undefined
 
var foo;
cs

 

 

여기서 일반적으로 생각하기에는 console.log가 실행되지 않아야하지만,  자바스크립트에서는 undefined라는 값을 읽을 수 있다.

이는 변수 선언이 소스코드가 실행되는 시점(런타임)이 아닌, 그 이전 단계에서 실행되기 때문이다. 변수 선언은 소스코드의 어디에 있든 상관없이 다른 코드보다 먼저 실행된다.

그래서 우리는 변수 선언이 어디에 위치하든 상관없이 어디서든지 변수를 참조할 수 있게 된다. 이처럼 변수 선언이 코드의 선두에서 실행되는 특징을 변수 호이스팅이라 한다.

 

단순한 변수 선언 뿐만 아니라 var, let, const, function, class 키워드를 사용해서 선언하는 모든 식별자가 호이스팅 된다는 사실.

다만, 착각하면 안되는 것은, 값의 할당은 소스코드가 실행되는 시점에 실행된다.

 

 

1
2
3
4
5
console.log(foo); //undefined
 
var foo = 50;
 
console.log(foo) //50
cs

 

 

함수 호이스팅에 대해서도 알아보면 좋다. 여기서는 자세히는 다루지 않겠다.

 

 

3. 함수레벨 스코프

 

지역 스코프 = 함수 몸체 내부를 말한다. 지역은 지역 스코프를 만든다.

 

보통의 프로그래밍 언어와 달리 자바스크립트에서 var 키워드로 선언된 변수는 오로지 함수의 코드블록만을 지역 스코프로 인정한다.

바꾸어 말하면 함수 이외의 코드블록에서 선언된 변수는 전역변수로 간주한다.

 

 

1
2
3
4
5
6
7
var x = 1;
 
if(true) {
    var x = 10;
}
 
console.log(x); //10
cs

 

여기서 if 문 안에서 선언된 var는 코드 블록 내에서 선언되었다고 할 지라도 전역 변수이다.

이는 if 문 안이 아니더라도 마찬가지, x라는 같은 변수 명으로 선언 및 할당이 두번 실행되었는데도 오류없이 동작한다.

때문에 if문 실행 후 x가 참조하고 있는 값이 변경 되었다. 이로 인해 실제 개발 환경에서 의도치 않은 변경이 일어날 수 있다.

또 다른 예제를 보자.

 

 

1
2
3
4
5
6
7
var i = 10;
 
for(var i=0; i<5; i++) {
    console.log(i);
}
 
console.log(i) // 5
cs

 

for문 실행이 종료되고 나서 전역변수인 i에 할당된 값이 변경되었다.

자바스크립트가 함수 레벨 스코프를 지원한다는 점을 확실하게 알 수 있을 것이다.

 

 

👉 결론적으로 var 키워드 사용은  다음과 같은 단점들을 가지고 있다

 

🔸변수 호이스팅에 의하여 런타임 이전에 변수가 선언되어 프로그램의 흐름과 가독성을 해칠 수 있다.
🔸이미 선언되고 값이 할당된 변수명으로 중복 선언해도 오류가 발생하지 않아 변수 값이 변경된다.
🔸함수가 아닌 코드블록내에서 선언한 변수를 모두 전역변수로 간주함으로써 전역변수가 남발되고 변수가 중복선언될 수 있다.

 

 

 

이를 보완하기 위해서 우리는 let과 const 라는 키워드를 사용할 수 있다.

 

 

4. let 키워드

 

let은 기본적으로 변수가 중복선언되지 않고, 블록레벨 스코프를 따른다. 다음 예제를 보자.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
let num = 1;
let num = 2//에러 발생
 
 
let foo = 3//전역변수
 
if(true){
 
    let foo = 4//지역변수
}
 
console.log(foo) //3
cs

 

 

같은 이름의 변수를 선언하려고 하면 에러가 발생한다.

또, if문 블록안에서 선언한 변수는 블록 안에서만 유효한 지역변수이다.

 

변수가 호이스팅되면 선언과 동시에 초기화되는 var와 달리, let은 선언 단계와 초기화 단계를 분리하여 동작한다.  호이스팅이 아예 발생하지 않는 것은 아니다. 다만 호이스팅이 발생하지 않는 것처럼 흉내내고 있다.

런타임 이전에 선언 단계는 실행되지만 초기화 단계는 변수 선언문에 도달했을 때 실행된다. 변수 선언 ~ 초기화 단계 까지는 변수를 참조할 수 없으므로 이를 일시적 사각지대라고 부른다.

 

 

1
2
3
4
5
6
7
8
9
//변수가 선언되었지만 참조를 할수는 없다.
//일시적 사각 지대
console.log(foo); //참조 에러
 
let foo; //변수 초기화 단계
console.log(foo) // undefined
 
foo = 1;
console.log(foo); //1
cs

 

let 에서도 변수 호이스팅 자체는 발생한다. 블록 안에서 전역변수를 참조하는 예제를 보자.

 

1
2
3
4
5
6
let foo = 1//전역변수
 
{
    console.log(foo); //참조 에러
    let foo = 2//
}
cs
 

 

호이스팅이 아예 발생하지 않았다면, console은 전역변수 foo의 값을 출력해야한다. 하지만 지역변수인 foo가 선언된 상태이므로 참조 에러가 발생하게 된다.

 

5. const 키워드

 

1
const num = 1;
cs

 

 

 

const 키워드를 사용할때는 변수 선언과 동시에 초기화가 필요하다. 그렇지 않으면 에러가 발생한다. let과 마찬가지로 블록레벨 스코프를 가지고, 변수의 중복선언이 허용되지 않는다.  또한 변수 호이스팅이 발생하지 않는 것처럼 동작한다.

 

let과 구별되는 점은 재할당 금지라는 것이다. 변수에 값을 재할당 할 수 없다.

 

 

1
2
const num = 1;
num = 2//에러
cs

 

 

변수와 반대되는 개념인 상수를 생각하면 된다.  상수도 메모리를 확보하여 값을 저장하지만, 원시 값을 할당한 경우에 할당된 값을 변경할 수 없다. 상수는 프로그래밍에서 유지보수성을 향상시켜주므로 적극 사용하도록 하자.

 

그렇다면, const 키워드에 값을 재할당하는 방법은 없을까?

 

프로퍼티를 통해 값을 변경할 수 있다. 재할당이 금지됐을 뿐, 불변을 의미하지는 않는다.

 

1
2
3
4
5
6
7
const person = {
    name'Lee'
};
 
person.name = "Kim";
 
console.log(person) // {name:"Kim"}
cs

 

 

const로 선언된 변수에 name이라는 객체를 할당했다. 우리는 객체에 접근하는 방식으로 값을 변경할 수 있다.

 

 

✔️정리


🔹변수 호이스팅, 함수레벨 스코프, 변수의 중복선언 등의 이유로 var 키워드 사용을 지양하는 것이 좋다.

🔹let과 const는 변수 중복금지, 블록레벨 스코프를 따르며, 호이스팅이 발생하지 않는 것처럼 동작한다.

🔹let은 값의 재할당이 필요할 때, const는 변경되지 않는 상수가 필요할 때 사용하자.

🔹const 선언 = 초기화 ;

🔹const도 객체의 프로퍼티를 통해 값을 변경할 수 있다.