[JavaScript] const, let

참고 문서

테스트 환경 정보

  • const
    • Chrome 21/Edge 12/Firefox 36/Opera 9/Safari 5.1 이상에서 사용 가능
    • IE는 10 이하에서 사용 불가
  • let
    • Chrome 49/Edge 14/Firefox 44/Opera 17/Safari 10 이상에서 사용 가능
    • IE는 10 이하에서 사용 불가. 11에서 부분적 지원

개요

신규 키워드 constlet에 대해 정리한 글.

const

const는 1.5 이후 버전에서 사용가능한 키워드로, 상수를 정의할 때 사용하며 var 키워드를 대체할 수 있다.

const name1 = value1 [, name2 = value2 [, ... [, nameN = valueN]]];
const PI = 3.14;

const는 전형적인 '상수'답게 선언 시점에 반드시 초기화되어야 하며 선언 이후의 할당은 무시되거나 에러를 발생시킨다.

const ABC; // SyntaxError: missing = in const declaration, 초기화 필요함

const PI = 3.14; // 정상적인 선언
const PI = 3.14159265; // SyntaxError: redeclaration of const PI, 중복 선언 불가
PI = 3.141592; // TypeError: invalid assignment to const `PI', 재할당 불가
var PI = 3; // SyntaxError: redeclaration of const PI, 같은 이름의 변수 선언 불가

const는 객체 선언에도 사용할 수 있으나 객체의 프로퍼티는 보호되지 않는다:

const MY_OBJECT = {"key": "value"};
MY_OBJECT = {"OTHER_KEY": "value"}; // SyntaxError: redeclaration of const MY_OBJECT
MY_OBJECT.key = "otherValue";
console.log(MY_OBJECT); // Object { key: "otherValue" }

객체를 완전히 보호하고 싶다면 Object.freeze()를 사용할 것.

var icecream = Object.freeze({a: 1, b: 2})
console.log(icecream); // {a: 1, b: 2}
icecream.c = 3
console.log(icecream); // {a: 1, b: 2}

let

let은 1.7(ES2015)부터 신규 추가된 키워드다. const와 마찬가지로 var를 대체할 수 있다.

let var1 [= value1] [, var2 [= value2]] [, ..., varN [= valueN]];

let은 JS의 변수가 갖는 함수 유효범위를 블록 유효범위로 제한하기 위해 사용한다.

다음의 경우:

function fn() {
  let a = 1;
  {
    let b = 2;
    var w = 102;
  }
  console.log(a); // 1
  console.log(w); // 102
  console.log(b); // ReferenceError: b is not defined
}
fn();

유효범위가 함수 단위인 w는 블록 밖에서도 참조 가능한 반면, let으로 선언한 변수 b는 해당 지역을 감싼 블록과 그 하위의 중첩 블록에서만 유효한 블록 단위의 유효범위를 갖기 때문에 블록을 벗어난 지역에서는 참조 에러가 발생한다.

var arr = ['a', 'b', 'c'];

for (var ele1 in arr) {}
console.log(arr[ele1]); // 'c'

for (let ele2 in arr) {}
console.log(arr[ele2]); // ReferenceError: ele2 is not defined

let으로 선언된 ele2for문 밖에서 참조할 수 없다.

let과 const는 끌어올림 대상에서 제외

letconst는 공통적으로 끌어올림(hoisting, 호이스팅 혹은 선언 끌어올리기)의 적용을 받지 않는다:

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

console.log(b); // ReferenceError: can't access lexical declaration 'b' before initialization
let b;

console.log(c); // ReferenceError: can't access lexical declaration 'c' before initialization
const c = 3;

그래서 var와는 다르게, 선언 전에 참조할 경우 ReferenceError가 발생한다.

특이사항: for loop와 let

아래처럼 딱 한 바퀴만 도는 for loop가 있다고 하자:

function getSomeWithVar() {
  var fn;
  for (var i = 0; i < 1; ++i) {
    fn = () => {
      console.log(i);
    }
  }
  return fn;
}
getSomeWithVar()(); // 1

반환된 fn에서 참조하는 i는 클로저 스코프에 존재한다. 그리고 i의 값은 마지막 반복의 증감식++i이 수행된 1이다.

그런데 만약 ilet이라면?

function getSomeWithLet() {
  var fn;
  for (let i = 0; i < 1; ++i) {
    fn = () => {
      console.log(i);
    }
  }
  return fn;
}
getSomeWithLet()(); // 0

1이 아니라 0이다.

그래서 찾아봤는데: https://stackoverflow.com/questions/42556873/closure-let-keyword-javascript

because you use let each anonymous function refers to a different instance of x. There is a different instance on each iteration of the loop. This happens because let has a block-level scope instead of the global function scope that var has.

(이 글이 사실이라는 전제 하에) 발번역하면 각 루프가 서로 다른 인스턴스이며, 익명함수가 그 서로 다른 인스턴스를 참조하기 때문이라고 한다. (혼절)