const, let, var의 개념 비교

ES2015에서 let, const를 도입하게 된 배경

  • 대부분의 문제는 전역 변수로 인해 발생한다.
  • 전역 변수는 간단한 애플리케이션의 경우,
    사용이 편리하다는 장점이 있지만 불가피한 상황을 제외하고 사용을 억제해야 한다.
  • 전역 변수유효 범위(scope)가 넓어서 어디에서 어떻게 사용될 것인지 파악하기 힘들며,
    비순수 함수(Impure function)에 의해 의도하지 않게 변경될 수도 있어서 복잡성을 증가시키는 원인이 된다.
    따라서 변수의 스코프는 좁을수록 좋다.

ES6는 이러한 var 키워드의 단점을 보완하기 위해 letconst 키워드를 도입하였다.

let, const 변수와 블록 스코프

  • ES2015에서 도입된 let, const에는 이전의 변수와는 다른 몇 가지 특징이 있다.

먼저, letconst같은 이름을 갖는 변수의 재선언을 허용하지 않는다.

1
2
3
4
5
6
7
let foo = 1;
let foo = 2; // unknown: Duplicate declaration "foo"
const foo = 3; // unknown: Duplicate declaration "foo"

function func(param) {
let param = 1; // unknown: Duplicate declaration "param"
}
  • 변수가 선언되기 전에 참조하려고 하면 에러가 난다.
1
2
console.log(foo); // ReferenceError: foo is not defined
let foo = 1;
  • ES2015 이전에는 변수를 위와 같이 사용해도 에러가 나지 않았다.
    오히려 위와 같은 방식이 하나의 프로그래밍 기법으로 활용되기도 했다.
  • 하지만, 가독성을 해치고 유지보수를 어렵게 만든다는 이유 때문에
    ES2015에 들어와서 제약이 강화된 letconst가 도입된 것이다.

letconst가 바로 블록 스코프(block scope)를 갖는다는 것

1
2
3
4
if (true) {
let i = 0;
}
console.log(i); // i is not defined
  • if문, for문, while문, function, try/catch문 등의 구문을 사용하면 블록이 형성되어,
    그 안에서 let 또는 const를 통해 선언된 변수는 외부에서 접근할 수 없다.
  • 즉, 코드 블록 내부에서 선언한 변수는 지역 변수이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
for (let i = 0; i < 10; i++) {
console.log(i);
// 0
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8
// 9
}
console.log(i); // ReferenceError: i is not defined
  • 특별한 기능이 없는 블록을 만들 수 있다.
    객체와 유사하게 중괄호로 코드의 일부분을 둘러싸면 된다.
1
2
3
4
{
let i = 0;
}
console.log(i); // ReferenceError: i is not defined

var 변수와 함수 스코프

  • ES2015에서 let, const가 도입되기 전까지는, 모든 변수는 var 키워드를 통해 선언되었다.
  • varlet과 유사하게, 값을 다시 대입할 수 있는 변수이다.
  • var는 함수의 매개변수와 유사하게, 함수 스코프를 갖는다.
  • 함수 내에서 선언된 변수는 함수 내에서만 유효하며 함수 외부에서는 참조할 수 없다.
  • 즉, 함수 내부에서 선언한 변수는 지역 변수이며 함수 외부에서 선언한 변수는 모두 전역 변수이다.
1
2
3
4
5
function func() {
var foo = 1;
}
func();
console.log(foo); // ReferenceError: foo is not defined
  • var를 통해 선언된 변수는 let, const로 선언된 변수와는 다른 특징을 갖는다.
  • 먼저, var를 통한 변수 선언은 재선언을 허용한다.
1
2
3
var foo = 1;
var foo = 1;
// 아무런 에러도 발생하지 않는다.
  • var로 선언된 변수는 내부적으로 함수 혹은 파일의 맨 위로 끌어올려지는
    호이스팅(hosting) 현상이 일어난다.
  • 스코프 안에만 있다면 변수가 선언되기 전에도 해당 변수에 접근할 수 있다.
  • 변수의 선언만 위로 끌어올려질 뿐 값을 대입하는 과정은 코드의 순서에 맞게 이루어진다.
  • 따라서, 대입이 일어나기 전에 변수의 값을 읽으면 undefined가 불러와지게 된다.
1
2
3
4
5
6
7
8
9
console.log(foo); // undefined
var foo = 1;

function func() {
console.log(bar); // undefined
var bar = 1;
}

func();
  • 마지막으로, var 변수는 함수 스코프를 갖는다.
    즉, 함수가 아닌 블록에서 정의된 var 변수는 해당 블록 바깥에서도 유효할 수 있다.
1
2
3
4
5
6
7
8
function func() {
for (var i = 0; i < 10; i++) {
//...
}
console.log(i); // 10
}

func();
  • cf) 참고 varlet으로 변경했을 경우
    : 블록 스코프이기 때문에 for문 밖에서는 i를 찾을 수 없다.
1
2
3
4
5
6
7
8
function func() {
for (let i = 0; i < 10; i++) {
//...
}
console.log(i); // ReferenceError: i is not defined
}

func();
  • 함수가 아닌 블록에서 정의된 var 변수는
    해당 블록 바깥에서도 유효할 수 있다는 특징에 주의하지 않을 경우,
    중첩된 for 루프와 같이 블록이 중첩된 코드에서 의도치 않은 동작을 할 수 있다.
1
2
3
4
5
6
7
8
9
10
for (var i = 0; i < 3; i++) {
console.log("outer");
// outer
for (var i = 0; i < 3; i++) {
console.log("inner");
// inner
// inner
// inner
}
}
  • 위아래 두 i 변수는 같은 함수 스코프에서 정의된 같은 변수이다.
  • 바깥쪽 루프를 한 번 도는 동안, 안쪽 루프를 도느라 이미 i의 값이 3이 되어버렸다.
  • cf) 참고 varlet으로 변경했을 경우
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
for (let i = 0; i < 3; i++) {
console.log("outer");

for (let i = 0; i < 3; i++) {
console.log("inner");
}
}
// outer
// inner
// inner
// inner
// outer
// inner
// inner
// inner
// outer
// inner
// inner
// inner
const let var
스코프 블록 스코프 블록 스코프 함수 스코프
재대입 X O O
재선언 X X O
호이스팅 X X O
사용 권장 1순위 2순위 3순위

참고링크