이전 글
var 키워드의 단점을 보완하기 위해 등장한 const, let 키워드는 어떤 특성을 가지는지 알아보자.
let 키워드
1. 동일한 식별자를 중복 선언할 수 없다.
var a = 1;
let a = 1; // 'a' has already been declared.
var 키워드와 달리, let 키워드는 동일한 식별자 변수를 중복 선언하면 에러가 발생한다.
2. 블록 레벨 스코프를 가진다.
앞서 var 키워드는 함수 레벨 스코프를 가진다고 했다.
반면 const, let 키워드는 블록 레벨 스코프를 가지는데, 모든 코드 블록을 지역 스코프로 인정한다.
코드 블록의 종류에는 단순 {}, 함수, if, switch 조건문, for, while 반복문, try catch 문 등이 있다.
let a = 1;
if(a ===1){
let k = 1;
console.log(k); // 1
}
console.log(k); // ReferenceError : k is not defined
if 조건문 안에서 선언된 k는 블록 레벨 스코프를 가지므로, if 블록문 밖에서 참조할 수 없다.
3. 변수 호이스팅이 발생하지 않는다.
let 키워드를 사용하면 이전 글에서 소개한 변수 호이스팅이 발생하지 않는다.
console.log(a); // ReferenceError : a is not defined
let a;
호이스팅이 발생하지 않았기 때문에, a 변수를 호출한 코드 라인에서는 다음과 같이 참조 에러가 발생한다.
※ 사실 호이스팅이 발생하지 않는 것은 아니다.
var 키워드로 선언했던 변수를 생각해보면,
코드가 시작하기 전에 변수가 선언이 되면서 undefined로 초기화가 이뤄졌었다.
사실 let 키워드로 선언한 변수도 호이스팅이 발생한다.
똑같이 코드가 시작하기 전에 변수가 선언이 되지만, undefined로 초기화가 바로 이뤄지지 않는다.
변수 초기화는 변수가 실제로 선언된 위치에서 이루어지며, 그 전에 변수를 참조하려는 접근에는 참조 에러를 발생시킨다.
선언부터 초기화 되기 전까지 참조를 할 수 없는 구간을 TDZ(Temporal Dead Zone)이라고 부른다.
아래의 코드를 바탕으로 다시 이해해보자.
function func(){
console.log(k);
let k = 1;
}
func(); // ReferenceError : Cannot access 'k' before initialization.
위에서 봤던 참조에러와는 조금 다른데,
"k가 초기화 되기 전에 접근할 수 없다." 라는 에러 메시지를 볼 수 있다.
이는 분명 함수가 시작할 때, 호이스팅이 일어나 k라는 변수 자체가 선언이 되었다는 의미이다.
사실 알고보면 호이스팅은 발생하지만, 호이스팅이 발생하지 않는 것처럼 느끼게 하므로
자연스럽게 개발자는 변수가 선언된 코드 라인 이후에서 변수를 사용하면 된다.
물론, const 키워드도 동일하게 동작한다.
4. 전역 객체에 할당되지 않는다.
브라우저 환경에서 window, NodeJS 환경에서 global 객체에 대해 들어봤을 것이다.
이는 전역 객체라고 하는데, 전역 변수로 선언한 변수들은 암묵적으로 전역 객체에 들어간다.
짧게 말하자면,
전역에 선언되었다는 것은 프로그램 전반적으로 모두 영향을 미칠 수 있다는 의미다.
변수 생명 주기, 스코프 등등 여러 문제가 발생할 위험이 많으므로,
필요하지 않다면 가급적 피하는 것이 좋다.
var a = 1;
console.log(a); // 1
console.log(window.a); //1
let b = 2;
console.log(b); // 2
console.log(window.b); // undefined
var 변수는 선언과 동시에 전역 객체(브라우저에서의 window)의 프로퍼티가 된다.
다행인 것은 let 키워드를 사용하면 이를 막을 수 있다.
5. 값을 변경할 수 있다.
아래에서 알아볼 const 변수와는 달리, let 키워드는 값을 변경할 수 있다.
그러므로 변수의 값이 변해야 할 경우에는 var 키워드가 아닌 let 키워드를 사용하면 된다.
let a = 1;
console.log(a); // 1
a = 3;
console.log(a); // 3
Const 키워드
1. 변수에 값의 재할당이 금지된다.
const 키워드는 constant 라는 이름 답게 변하지 않는 변수를 만들고자 할 때 사용된다.
const 변수는 한번 초기화되면 값을 바꿀 수 없으므로, 선언과 동시에 초기화를 해야 한다.
const a; // SyntaxError : Missing initializer in const declaration
const b = 1; // ok!
b = 3; // TypeError : Assignment to constant variable.
다음과 같이 const로 선언된 변수는 재할당이 금지된다.
자바스크립트에서 원시값(String, Number, Boolean, undefined, NaN, null)은 변경을 할 수 없는데,
const 키워드와 원시값이 만나면 그 변수는 재할당도 불가능하고 값 변경도 불가능하다.
자바스크립트에서 원시값 변경이 왜 안되냐 궁금할 수 있겠다.
let a = 1;
a = 2;
이렇게 당연히 값이 바뀌는데?
사실 이는 1이라는 값 자체를 2로 변경하는 것이 아니라,
1이 선언된 공간을 가리키는 a 변수가, 새로이 선언된 다른 공간에 있는 2를 가리키는 것으로 바뀐 것일 뿐이다.
다른 예시로,
let a = "abc";
console.log(a); // abc
a[0]="A";
console.log(a); // abc
a = "Abc";
console.log(a); // Abc
다음과 같이 문자열의 일부 문자를 바꿔도 변하지 않는 것을 경험했을 것이다.
이는 위와 마찬가지로 자바스크립트가 원시값 변경을 허용하지 않기 때문이다.
아무튼,
const 키워드와 원시값이 만날 때는 재할당이 불가능하며 할당된 값 변경도 불가능하다.
하지만 const 키워드와 객체가 만났을 때는 또 다르다.
2. 재할당이 금지되었다고 해서 불변의 변수인 것은 아니다.
const 키워드의 변수에 객체가 할당되었다면, 우린 할당된 객체의 값을 바꿀 수 있다.
원시값은 변경이 불가능한데 비해, 객체는 변경이 가능하다.
참조 변수에 해당하는 특성을 이용해 설명해보면,
변수에 객체를 할당하면, 변수에서는 객체의 실제 값들이 아닌 주소값만을 가진다는 것을 알고 있을 것이다.
그러므로 변수는 할당된 객체의 주소를 기억하고 있다.
const obj = {name : "Jiho"};
console.log(obj); // {name : "Jiho"}
obj.age = 25;
console.log(obj); // {name : "Jiho", age : 25}
obj = {name : "Thomas"} // TypeError : Assignment to constant variable.
const 키워드로 선언한 변수에 객체가 할당되어 있다면, 위와 같이 객체 변경이 가능하다.
물론 마지막 코드라인과 같이 obj에 새로운 객체를 재할당하는 것은 불가능하다.
다음과 같은 특성을 바탕으로 const 키워드로 선언한 변수가 가장 안전하다고 할 수 있겠다.
이와 같이 const 키워드로 선언한 객체는 변경이 가능한 것을 개발자가 모른다면,
자연스럽게 let 키워드로 객체를 선언 할 것이다.
이는 또한 의도치 않은 재할당이 발생하여 프로그램에 에러를 발생시킬 수도 있다.
최종 결론
1. 변수를 선언할 때 우선 const 키워드로 선언하고, 재할당이 필요해 보이는 변수만을 let 키워드를 고려하는 것이 좋다.
2. 프로젝트에서 ES6 문법을 사용할 수 있다면 var 키워드 없이 변수 선언을 const, let으로만 수행하자.
3. 다음과 같은 각 키워드의 특성을 먼저 이해해야 효율적인 코드 작성이 가능하다.
'JavaScript > theory' 카테고리의 다른 글
[자바스크립트] 일급 객체? 함수! (0) | 2021.07.24 |
---|---|
[자바스크립트] ?. 연산자, 옵셔널 체이닝 (0) | 2021.07.08 |
[자바스크립트] var 키워드를 사용하지 말아야 하는 이유 (0) | 2021.05.21 |
[자바스크립트] 렉시컬 스코프 (0) | 2021.05.19 |
[자바스크립트] 동등 비교 연산자 "==" 와 일치 비교 연산자 "===" (0) | 2021.05.03 |