[JavaScript] var, let, const 의 차이점

Dico·2020년 6월 29일
2

[JavaScript]

목록 보기
4/21

자바스크립트의 유연성에서 오는 단점을 보안하기 위해 ES2015에서 let, const가 추가되었다.
(부끄럽지만 아직 나의 코드리뷰에는const 를 쓰라는 코멘트가 달리고 있기에...🙈)

var, let, const 의 차이점을 정리해보려 한다.


var, let, const의 재선언과 재할당

var은 아래와 같이 재선언, 재할당이 모두 가능하다.

var name = 'Kevin'; 
console.log(name);   //"Kevin"

var name = 'Jessica'; 
console.log(name);   //"Jessica"

name = 'Jessie'; 
console.log(name);   //"Jessie"

let은 재선언은 되지 않으며, 재할당만 가능하다.

let fruit = "melon"; 
console.log(fruit);  //"melon"

let fruit = "banana"; 
console.log(fruit);  /*"SyntaxError: Identifier 'fruit' has already 
been declared"*/

fruit = "strawberry";
console.log(fruit);  //"strawberry"

const재선언과 재할당 모두 불가능하다. 따라서 상수(변하지 않는 값)에 사용한다. 또한 const반드시 선언과 동시에 할당이 이루어져야 한다.
+const와 같이 immutable value(상수)대문자로 선언하는 것이 일반적이다.

const CAT;           /*"SyntaxError: Missing initializer in const declaration"*/

const CAT = "Meow"; 
console.log(CAT);    //"Meow"

const CAT = "Woof"; 
console.log(CAT);    /*"SyntaxError: Identifier 'cat' has already been 
declared"*/

CAT = "Woof"; 
console.log(CAT);    //"TypeError: Assignment to constant variable."

+const로 선언된 변수에 재선언과 재할당은 불가능하나, const에 할당된 객체(배열 및 함수 포함)에 변화를 주는 것은 가능하다.

//이건 가능!
const FRUITS = ["apple", "banana", "grape"];

FRUITS = ["strawberry", "kiwi", "melon"]; /*Uncaught TypeError: Assignment 
to constant variable.*/

FRUITS[2] = "orange";

console.log(FRUITS); //["apple", "banana", "orange"]


//이것도 가능!
const ARR = [1, 2, 3];

ARR.length = 0;

console.log(ARR);   //[]

scope 내의 var, let, const의 차이점

1. 스코프 레벨 (scope level)

함수레벨스코프(function-level scope): 함수 내에서 선언된 변수는 함수 내에서만 유효하며 함수 외부에서는 참조할 수 없다. 즉, 함수 내부에서 선언한 변수는 지역 변수이며 함수 외부에서 선언한 변수는 모두 전역 변수이다.

블록레벨스코프(block-level scope): 모든 코드 블록(함수, if문, for문, while문, try/catch문 등) 내에서 선언된 변수는 코드 블록 내에서만 유효하며 코드 블록 외부에서는 참조할 수 없다. 즉, 코드 블록 내부에서 선언한 변수는 지역 변수이다.

-poiemaweb.com

varlet 의 첫번째 차이점은 scope!

var함수레벨스코프로, 함수의 코드 블록만을 스코프로 인정한다. 따라서 for문의 변수 선언문에서 선언한 변수를 for문 밖에서도 참조할 수 있고, if문 안에서 선언된 변수도 밖에서 참조할 수 있다.

var x = 5; 
if(true) { 
  	var y = 2; 
}

console.log(x * y);   //10

if문 내부에 있다해도 x와 y는 모두 전역변수이기 때문이다.

같은 이유로 함수블록 안에서 전역스코프 내 변수와 동일한 이름을 사용한다해도 override되지 않는다.

var outerWear = "T-Shirt"; 

function myOutfit() { 
  var outerWear = "Sweater";
  return outerWear;
}

console.log(myOutfit());      //Sweater
console.log(outerWear);       //T-Shirt

하지만 letconst블록레벨스코프를 따른다. let의 스코프를 결정하는 것은 중괄호{}이다. 중괄호를 기준으로 스코프가 정해지기 때문에 중괄호 안에서 선언된 letconst는 바깥에서 쓸 수가 없다.

for(let i = 0; i < 5; i++) { 
  a = i + 2; 
  console.log(i);    //0, 1, 2, 3, 4
}

console.log(i);      //ReferenceError: i is not defined 

위 예문의 조건문에서 선언된 let은 for문 안에서만 쓸 수 있도록 선언된 것으로, 전역스코프의 console.log(i)로는 접근할 수가 없다.

2. window 객체 요소 추가

var를 이용해 전역변수를 만들면 window객체에 key와 value로 추가되지만,
let이나 const를 이용해 전역변수를 만들면 window 객체에 추가되지는 않는다.

var a = "apple"; 
console.log(window.a);      //"apple"

let b = "banana"; 
console.log(window.b);      //undefined

const c = "strawberry";
console.log(window.c);      //undefined

3. 호이스팅(hoisting)

호이스팅: 자바스크립트는 ES6에서 도입된 let, const를 포함하여 모든 선언(var, let, const, function, function*, class)을 호이스팅한다. 호이스팅(Hoisting)이란, var 선언문이나 function 선언문 등을 해당 스코프의 선두로 옮긴 것처럼 동작하는 특성을 말한다.

-poiemaweb.com

var로 선언된 변수와 let으로 선언된 변수는 호이스팅에서 다음과 같은 차이가 나타난다.

console.log(she); //undefined
var she; 

console.log(he);  //Cannot access 'he' before initialization
let he; 

이러한 차이가 나타나는 이유는 뭘까? let은 호이스팅이 되지 않는 것일까?

그렇지 않다.

앞서 인용된 문장과 같이 자바스크립트는 let, const를 포함하여 모든 선언(var, let, const, function, function*, class)을 호이스팅한다.
자세히 들여다보면, error메세지 상의 Cannot access호이스팅이 되어 밑에 let이 존재한다는 것을 알고있다는 뜻이 된다.

이 부분을 이해하려면 let으로 변수가 생성되는 단계를 알 필요가 있다.
let으로 3단계에 걸쳐 변수를 생성한다.

1) 선언 단계(Declaration phase)
변수를 실행 컨텍스트의 변수 객체(Variable Object)에 등록한다. 이 변수 객체는 스코프가 참조하는 대상이 된다.

2) 초기화 단계(Initialization phase)
변수 객체(Variable Object)에 등록된 변수를 위한 공간을 메모리에 확보한다. 이 단계에서 변수는 undefined로 초기화된다.

3) 할당 단계(Assignment phase)
undefined로 초기화된 변수에 실제 값을 할당한다.

-poiemaweb.com

선언과 동시에 초기화가 이루어지는 var와는 다르게 let해당 선언문이 실행되기 전까지 초기화 단계가 이루어지지 않는다. 초기화로 값이 정해지지 않은 let에 접근 시 위 예제와 같이 ReferenceError: Cannot access before initialization 에러가 발생한다.

이렇게 let으로 변수 선언 후 실행되기 전 사이의 구간을 ❗️Temporal Dead Zone(TDZ)❗️이라고 한다.

Wrap up

변수 선언 시, 가독성과 유지보수의 편의를 위해서는 기본적으로 const를 사용한다(상수라면 특히 더!). 이는 의도치 않은 재할당을 방지해준다.

let은 재할당이 필요한 경우에 한하여 사용하는 것이 좋으니 (재선언이 아닌)재할당이 필요하다고 판단될 때 constlet으로 바꿔주면 된다.

ES6를 따른다면var사용은 권장하지 않는다.


Reference

*본 포스팅은 아래 사이트들을 참고 및 인용하여 작성되었습니다.
학습단계로 잘못된 정보가 있을 수 있습니다. 잘못된 부분에 대해 알려주시면 곧바로 정정하도록 하겠습니다 😊
https://poiemaweb.com/es6-block-scope
https://velog.io/@bathingape/JavaScript-var-let-const-%EC%B0%A8%EC%9D%B4%EC%A0%90
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#Temporal_dead_zone

profile
프린이의 코묻은 코드가 쌓이는 공간

0개의 댓글