ECMAScript 6 Block binding

Block Binding

Javascript의 전통적인 식별자(Identifier - 변수, 상수, 이름 있는 함수 등) 선언이 작동하는 방식은 JavaScript 프로그래밍의 까다로운 부분 중 하나였습니다. 대부분의 C 기반 언어에서 식별자(Identifier - 변수 바인딩)는 선언이 발생한 지점에 만들어집니다. 그러나 JavaScript에서는 그렇지 않습니다. 식별자(Identifier)를 실제로 생성하는 위치는 선언하는 방법에 따라 다르며, ECMAScript 6에서는 제어 Scope을 더 쉽게 만들 수 있는 옵션을 제공합니다. 이 챕터에서는 고전적인 var 식별자 선언이 왜 혼란스럽게 하는지와, ECMAScript 6의 Block level binding의 소개, 그리고 이를 사용하는 몇 가지 모범 사례를 제시합니다.

var 식별자 선언 및 Hoisting(호이스팅)

식별자

JavaScript의 식별자는 간단히 말해서 이름입니다
JavaScript 식별자는 변수, 상수, 함수에 이름을 붙이거나 자바스크립트 코드 내 루프 문에 레이블을 붙이는 데 사용됩니다.
올바른 식별자가 되기 위한 규칙은 자바나 다른 수많은 언어의 규칙과 동일합니다.
첫 번째 문자는 알파벳(letter), 밑줄(_) 혹은 달러표시($) 여야 한다. 이어지는 문자들은 알바벳(letter), 숫자, 밑줄(_) 혹은 달러 표시여야 한다.
JavaScript가 숫자와 식별자를 쉽게 구별할 수 있게 하기 위해, 숫자는 첫 번째 문자로 허용되지 않는다.

출처 http://www.devholic.net/1000687

var를 이용한 식별자 선언은 실제 선언이 어디에서 발생하는지에 관계없이 해당 Scope의 맨 위에있는 것처럼 처리됩니다. (또는 함수 외부에서 선언된 경우 전역 Scope로 처리됩니다.) 이것을 JavaScript에서 Hoisting이라고합니다. 아래 예제 함수를 보면 Hoisting에 대해 쉽게 이해할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
function getValue(condition) {
if (condition) {
var value = "blue";
// 다른 코드들
return value;
} else {
// value는 여기에서 'undefined' 입니다.
return null;
}
// value는 여기에서 'undefined' 입니다.
}

JavaScript에 익숙하지 않은 경우 위 코드에서 if 조건이 true로 평가되는 경우에만 value가 생성될 것으로 예상할 수 있지만 실제로 value 변수는 선언한 위치와 관계없이 생성됩니다. JavaScript 엔진은 getValue 함수를 다음과 같이 변경합니다.

1
2
3
4
5
6
7
8
9
10
function getValue(condition) {
var value; // 변수의 선언 위치가 바뀝니다.
if (condition) {
value = "blue"; // 변수의 초기화는 여기에서 이뤄집니다.
// 다른 코드들
return value;
} else {
return null;
}
}

value 변수의 선언은 Scope 맨 위로 올라가고 변수의 초기화는 동일한 지점에 남아 있습니다. 즉, valueelse 절에서 실제로 액세스할 수 있습니다. 하지만 else 구문에서 접근할 경우 변수는 초기화되지 않았기 때문에 undefined값을 갖게됩니다.

새로 JavaScript를 공부하는 개발자가 Hoisting에 익숙해지기까지 어느정도 시간이 걸리며, 이 독특한 동작을 잘못 이해하면 버그가 발생할 수 있습니다. 이러한 이유로 ECMAScript 6에는 식별자(Identifier)의 생명주기 제어를 좀 더 강력하게 만들기 위한 Block level scope 옵션이 도입되었습니다.

Block-Level 식별자 선언

Block level 식별자 선언은 지정된 Block scope 외부에서 액세스할 수없는 식별자(Identifier)를 정의하는 방법입니다. 이런 방식은 Lexical scope이라고도 하는 Block scope이 만들어집니다.

  1. Function 내부
  2. Block 내부 ( {} 문자 사이를 나타냄)

Block scope은 C 기반 언어의 작동 방식이며 ECMAScript 6의 Block-level 식별자(Identifier) 선언은 JavaScript에 C 기반 언어와 동일한 유연성(균일성)을 제공하기 위한 것입니다.

let 식별자 선언

let을 이용한 식별자 선언 구문은 var 구문과 동일합니다. 기본적으로 varlet으로 바꾸면 똑같이 식별자를 선언할 수 있지만 Scope이 현재 코드 Block으로 제한됩니다. (하지만 잠시후에 논의할 약간의 차이점은 있습니다). let 선언은 자동으로 Block의 맨 위로 올려지지 않기 때문에 let 선언을 Block의 최상단에 배치해야만 Block 전체에서 사용할 수 있습니다. 아래의 예제를 살펴 보도록 하겠습니다.

1
2
3
4
5
6
7
8
9
10
11
function getValue(condition) {
if (condition) {
let value = "blue";
// 다른 코드들
return value;
} else {
// value 변수는 이 Block에 존재하지 않습니다.
return null;
}
// value 변수는 이 Block에 존재하지 않습니다.
}

이 버전의 getValue 함수는 다른 C 기반 언어의 실행 방식과 매우 유사합니다. 변수 valuevar 대신에 let을 사용하여 선언 되었기 때문에, 변수 선언이 함수 정의 맨위로 끌어 올려지지 않습니다. 그리고 valueif을 벗어나면 더 이상 접근할 수 없습니다. if 조건이 false로 평가되면 value 변수는 선언이 되지 않고 값의 초기화도 이뤄지지 않습니다.

재선언 안됨

식별자(변수)가 이미 Scope내에서 정의된 경우 해당 Scope에서 같은 이름으로 let 선언을 다시하면 오류가 발생합니다. 예를 들어 아래의 코드는 에러가 발생합니다.

1
2
3
var count = 30;
// 구문 오류
let count = 40;

위의 예제에서 count는 두 번 선언되었습니다(한 번은 var, 다른 한 번은 let). let은 이미 선언된 식별자를 동일한 Scope에 다시 정의하지 않기 때문에 let 변수 선언은 오류를 발생시킵니다. 반면에 아래 예제와 같이 let 선언이 포함된 코드가 새로운 Scope에서 동일한 이름의 식별자로 선언될 경우 오류가 발생하지 않습니다.

1
2
3
4
5
6
var count = 30;
// 에러가 발생하지 않습니다.
if (condition) {
let count = 40;
// 다른 코드들
}

위 코드의 let 선언은 if 블럭에 count라는 새로운 변수를 생성하기 때문에 오류를 발생시키지 않습니다. if 블럭 안에서 선언한 count 변수가 전역 count 변수를 shadows 처리하여 Block 내부에서는 전역 count에 대한 암시적 접근을 차단합니다.

Constant 선언

ECMAScript 6에서 const 선언 구문을 사용하여 식별자를 정의할 수있습니다. const를 사용하여 선언된 식별자는 상수(Constant)로 간주되며, 일단 값을 설정하면 변경할 수 없습니다. 이런 이유로 모든 const 식별자는 다음 예제와 같이 선언시 초기화 되어야합니다.

1
2
3
4
5
// 유효한 상수 선언
const maxItems = 30;
// 초기화가 빠져있어서 구문 오류
const name;

위 예제에서 maxItems 변수는 초기화 코드가 있어서 const 선언이 문제없이 실행됩니다. 그러나 name 변수는 초기화되지 않았으므로 만약 이 코드가 포함된 프로그램을 실행하려고 하면 name 변수 선언에서 구문 오류를 일으킬 수 있습니다.

상수와 let 선언 관계

let 선언 변수는 Block-level 변수 선언입니다. 이것은 프로그램의 실행 흐름이 변수가 선언된 Block을 벋어 나면 변수를 더이상 액세스할 수 없다는 것을 의미합니다. 아래의 예제에서 보듯이, 선언된 변수가 Hoisting되지 않습니다.

1
2
3
4
5
6
if (condition) {
const maxItems = 5;
// 다른 코드
}
// maxItems은 여기에서 액세스할 수 없습니다.

이 코드에서 변수 maxItemsif 블럭 안에서 선언되었습니다. 그리고 명령문 실행이 끝나면 maxItems 변수는 해당 블럭 외부에서 액세스할 수 없습니다. let과 동일하게 같은 Scope에 이미 정의된 식별자 이름을 또 사용하면 const 선언시 오류가 발생합니다. 변수가 var (전역 또는 함수 Scope의 경우) 또는 let (Block scope의 경우)을 사용하여 선언되었는지는 중요하지 않습니다. 예를 들어 다음 코드를 살펴 보겠습니다.

1
2
3
4
5
6
var message = "Hello!";
let age = 25;
// 에러가 발생합니다.
const message = "Goodbye!";
const age = 30;

위 예제에서 상위 두개의 const 선언을 단독으로 사용하면 유효하지만, 위 코드에서는 앞의 varlet 선언이 있어서 오류가 발생합니다.

letconst가 이러한 비슷한 점이 있지만 주요한 큰 차이점이 하나 있습니다. 아래의 예제에서 보듯이 이전에 정의된 const 식별자에 새로운 상수값을 할당하려고 하면 strict 모드와 non-strict 모드 모두에서 오류가 발생합니다.

1
2
const maxItems = 5;
maxItems = 6; // 에러가 발생합니다.

다른 언어의 상수와 매우 비슷하게 나중에 maxItems 상수에 새로운 값을 할당할 수 없습니다. 그러나 다른 언어와 달리 JavaScript의 상수가 객체인 경우 객체의 프로퍼티값은 수정할 수 있습니다.

const를 이용한 객체 선언

const 변수 선언은 값 자체가 아닌 바인딩의 수정을 방지합니다. 이는 객체의 const 선언이 객체의 수정을 막지 않는다는 것을 의미합니다. 예를 들어 아래 코드를 살펴 보겠습니다.

1
2
3
4
5
6
7
8
9
10
const person = {
name: "Nicholas"
};
// 실행됨
person.name = "Greg";
// 에러 발생
person = {
name: "Greg"
};

위 코드에서 person 변수는 하나의 프로퍼티를 가진 JavaScript 객체이고, 초기값을 가지고 생성됩니다. person.name의 값을 변경하는건 오류 없이 가능합니다. 왜냐하면 person에 바인드된 값을 변경하는 것이 아니고 person에 포함된 것을 변경하기 때문입니다. 만약 코드에서 person에 값을 할당하려고 시도하면 (바인딩을 변경하려고 하면) 오류가 발생합니다. const가 객체와 어떻게 작동하는지에 대한 이 미묘한 점이 오해하기 쉽습니다. const는 바인딩 값의 수정이 아니라 바인딩 자체의 수정을 방지한다는 것을 기억 해야 합니다.

TDZ (The Temporal Dead Zone)

let 초기화와 관련하여 발생하는 오류를 가리켜 TDZ(TEMPORAL DEAD ZONE) 오류라고 부릅니다.

let 또는 const로 선언된 식별자는 선언 전에는 액세스할 수 없습니다. 아래의 예제와 같이 일반적으로 안전한 typeof 연산자를 사용하는 경우에도 참조 오류가 발생합니다.

1
2
3
4
if (condition) {
console.log(typeof value); // ReferenceError!
let value = "blue";
}

여기서 식별자 valuelet을 사용하여 정의되고 초기화 되지만 이전행에서 오류가 발생하므로 해당 명령문은 실행되지 않습니다. value 식별자가 JavaScript 커뮤니티에서 불리는 The Temporal Dead Zone(TDZ)에 존재한다는 것이 문제입니다. TDZ는 ECMAScript 사양에서 명시적으로 설명하고 있지는 않지만, 식별자를 letconst 선언전에 액세스할 수없는 이유를 설명할 때 자주 이용됩니다. 여기에서 TDZ를 발생시키는 선언 배치의 미묘한 부분에 대해 설명합니다. 모든 예제에서 let을 사용하지만 const에도 동일하게 적용됩니다.

JavaScript engine이 실행될 때에 블록({ })에서 선언된 변수를 찾아 선언된 식별자가 var 선언자를 이용한 선언이면 변수의 선언을 함수의 최상단 또는 전역 Scope으로 가져가고, let 또는 const 선언자로 선언된 경우 변수의 선언을 TDZ에 배치합니다. TDZ의 변수에 대한 액세스는 런타임 오류가 발생합니다. 이러한 변수를 TDZ에서 제거하고 변수를 선언된 이후에 사용하면 안전하게 사용할 수 있습니다.

let 또는 const 선언자로 선언된 변수는 사용하기 전에 defined 됩니다. 이런 부분은 이전 예제에서 보여줬던 안전한 연산자인 typeof에도 적용이 됩니다. 그리고 변수가 선언된 블록의 외부에서 변수에 대해 typeof를 사용할 수는 있지만 정상적인 결과를 받지 못합니다. 다음 코드를 살펴보겠습니다.

1
2
3
4
5
console.log(typeof value); // "undefined"
if (condition) {
let value = "blue";
}

value가 선언된 블록 외부에서 사용되기 때문에 typeof 연산자가 실행될 때 변수 value는 TDZ에 없습니다. 이 의미는 value 변수에 바인딩이 없기 대문에 typeof 연산자는 "undefined"를 반환합니다.

TDZ는 Block-binding의 독특한 부분입니다.그리고 또 다른 독특한 부분은 루프 내부에서의 사용과 관련이 있습니다.

Loop 안에서의 Block Binding

개발자가 Block-level scope을 가장 많이 원하는 곳 중 하나는 for loop 이고, 일반적으로 임시로 사용하는 카운터 변수는 루프 내부에서만 사용해야 합니다. 예를 들어 기존 JavaScript 코드에서 아래와 같은 코드는 보기 드물지 않습니다.

1
2
3
4
5
for (var i = 0; i < 10; i++) {
process(items[i]);
}
// i 는 여전히 여기에서 액세스 가능합니다.
console.log(i); // 10

Block level scope가 기본인 다른 언어에서는 이 예제가 의도 한대로 실행되고, for loop 내부에서만 i 변수에 액세스할 수 있습니다. 그러나 JavaScript에서는 var 선언이 Hoisting되기 때문에 loop가 완료된 후에도 변수 i를 계속 액세스할 수 있습니다. 다음 코드와 같이 var 선언자 대신 let 선언자를 사용하여 원래 의도 한 동작이 실행 되도록 해야 합니다.

1
2
3
4
5
6
for (let i = 0; i < 10; i++) {
process(items[i]);
}
// i는 접근할 수 없기 때문에 error가 발생합니다.
console.log(i);

위 코드에서 변수 ifor loop 내에만 존재합니다. loop가 완료되면 변수는 더 이상 다른 곳에서 액세스할 수 없습니다.

loop 안의 함수

var 변수 선언자의 특징 중에 loop 내부에서 생성한 변수를 loop 밖에서 액세스할 수 있는 특징 때문에 loop 내부에서 함수를 만들때 오랫동안 문제가 있었습니다. 다음 코드를 생각해 보겠습니다.

1
2
3
4
5
6
7
8
9
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs.push(function() { console.log(i); });
}
funcs.forEach(function(func) {
func(); // 숫자 '10'이 열번 출력됩니다.
});

이 코드는 일반적으로 0에서 9까지의 숫자를 출력할 것으로 예상되지만 1010번 출력합니다. 왜냐하면 i는 반복문의 반복마다 서로 공유되기 때문입니다. 즉, loop 내부에서 생성한 함수는 모두 동일한 변수를 참조합니다. 변수 i는 loop가 완료되면 10의 값을 가지므로 console.log (i)가 호출될 때마다 10을 매번 출력합니다.

이 문제를 해결하기 위해 대부분의 개발자는 루프 내에서 즉시 호출되는 함수 표현식 (IIFE: Immediately-invoked function expression)을 사용하여 아래 예제에서 처럼 반복할 변수의 새 복사본을 강제로 만듭니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs.push((function(value) {
return function() {
console.log(value);
}
}(i)));
}
funcs.forEach(function(func) {
func(); // outputs 0, then 1, then 2, up to 9
});

위의 예제에서는 IIFE를 loop 내부에서 사용했습니다. 변수 i는 IIFE 형태로 전달되고, IIFE는 자체 복사본을 만들어 value로 저장합니다. 이것은 반복에서 함수가 사용하는 값이므로 각 함수를 호출하면 loop에서 0부터 9까지 카운트될 때 예상 값이 리턴됩니다. 하지만 ECMAScript 6에서는 letconst를 사용하여 Block-level 바인딩을 하면 이러한 작업이 단순화 됩니다.

Loop 안에서의 let 선언

let선언은 앞의 예제에서 IIFE가 수행하는 것을 효과적으로 대체함으로써 loop를 단순화합니다. 각 반복에서 loop는 새 변수를 만들고 이전 반복과 같은 이름을 가진 변수 값으로 초기화합니다. 즉, IIFE를 생략하고 다음과 같이 원하는 결과를 얻을 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
var funcs = [];
for (let i = 0; i < 10; i++) {
funcs.push(function() {
console.log(i);
});
}
funcs.forEach(function(func) {
func(); // outputs 0, then 1, then 2, up to 9
})

위 예제는 var와 IIFE를 사용했던 예제와 정확히 똑같이 실행되지만 훨씬더 깔끔합니다. let 선언은 매번 loop를 통해 새로운 변수 i를 생성하므로 loop 안에 생성된 각 함수는 i의 복사된 값을 갖습니다. 각 i의 복사본은 loop의 시작 부분에서 생성된 값이 할당됩니다. 다음 예제와 같이 for-in, for-of loop에도 똑같이 적용됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var funcs = [],
object = {
a: true,
b: true,
c: true
};
for (let key in object) {
funcs.push(function() {
console.log(key);
});
}
funcs.forEach(function(func) {
func(); // outputs "a", then "b", then "c"
});

이 예제에서 for-in loopfor loop와 같은 결과를 보여줍니다. loop가 실행될 때마다 새로운 key 바인딩이 생성되고 각 함수는 key 변수의 복사본을 가지고 있습니다. 함수가 다른 값을 출력하는 결과를 보여줍니다. 만약 keyvar 선언자를 이용해 선언하면 모든 함수는 동일한 "c"를 출력할 것입니다.

loop에서 let 선언 동작은 스펙에서 특별히 정의된 동작이고, let의 Non-Hoisting 특성과 반드시 관련이 없다는 것을 이해하는 것이 중요합니다. 실제로, let의 초기 구현은 이러한 동작을 하지 못했고, ECMAScript 6 명세 Process가 진행되면서 추가된 것입니다.

Loop 안에서의 상수 선언

ECMAScript 6 명세는 loop내부 const 선언을 명시적으로 허용하지 않습니다. 그러나 여러분이 사용중인 loop 종류에 따라 const 선언을 사용하여 다른 처리를 할 수 있습니다. 일반적인 for loop의 경우 초기화시 const를 사용할 수 있지만 첫 번째 이터레이션 후 오류를 던집니다.

1
2
3
4
5
6
7
8
var funcs = [];
// 첫 번째 이터레이션 후에 에러를 던짐
for (const i = 0; i < 10; i++) {
funcs.push(function() {
console.log(i);
});
}

위 예제에서 변수 i를 상수로 선언했습니다. i가 0 인 첫번째 loop의 반복은 성공적으로 실행됩니다. 하지만 i++가 상수를 수정하려하기 때문에 i++ 구문이 실행되변 에러가 발생합니다. 따라서 변수를 수정하지 않는 loop 초기화 부분의 변수를 선언할 때만 const를 사용할 수 있습니다.

반면에 for-in 또는 for-of loop에서 사용될 때 const 변수는let 변수와 똑같이 동작합니다. 다음 코드는 오류가 발생하지 않습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var funcs = [],
object = {
a: true,
b: true,
c: true
};
// 에러를 발생하지 않습니다.
for (const key in object) {
funcs.push(function() {
console.log(key);
});
}
funcs.forEach(function(func) {
func(); // outputs "a", then "b", then "c"
});

이 코드는 “Loop 안에서의 let 선언” 부분의 두 번째 예제와 거의 동일한 실행 결과를 보여줍니다. 유일한 차이점은 key의 값을 loop 내에서 변경할 수 없다는 것입니다. for-infor-of loop는 const와 함께 실행할 수 있습니다. loop 초기화는 기존 바인딩의 값을 수정하려고 시도하는 대신 loop가 반복할 때마다 새로운 바인딩을 생성하기 때문입니다 (이전 예제에서 에러가 발생한 이유는 for-in 대신 for loop을 사용했기 때문입니다.).

전역 Block Binding

letconstvar와 또 다른 부분은 전역 Scope 입니다. 전역 Scope에서 var을 사용할 때, 전역 객체 (브라우저의 window)에 대한 프로퍼티로 새로운 전역 변수를 만듭니다. 이는 실수로 var를 사용하여 기존 전역 변수의 값을 덮어 쓸 수 있음을 의미합니다.

1
2
3
4
5
6
// browser에서 실행.
var RegExp = "Hello!";
console.log(window.RegExp); // "Hello!"
var ncz = "Hi!";
console.log(window.ncz); // "Hi!"

글로벌 RegExpwindow에 정의되어 있더라도 var 선언으로 덮어 쓰는 것은 안전하지 않습니다. 이 예제는 원본을 덮어 쓰는 새로운 전역 변수 RegExp를 선언했습니다. 비슷하게 변수 ncz는 전역 변수로 정의되고 즉시 window에 프로퍼티로 정의됩니다. 이것이 항상 JavaScript가 작동하는 방법입니다.

전역 Scope에서 let 또는constvar 대신 사용하면 전역 범위에 새로운 바인딩이 생성되지만 전역 객체에 프로퍼티가 추가 되지는 않습니다. 즉 let 또는 const를 사용하여 전역 변수를 덮어 쓸 수 없다는 것을 의미합니다. 다음 코드를 살펴 보겠습니다.

1
2
3
4
5
6
7
8
// in a browser
let RegExp = "Hello!";
console.log(RegExp); // "Hello!"
console.log(window.RegExp === RegExp); // false
const ncz = "Hi!";
console.log(ncz); // "Hi!"
console.log("ncz" in window); // false

이 코드에서 RegExp에 대한 새로운 let 선언은 전역 변수 RegExp를 가리키는 바인딩을 만듭니다. 즉, window.RegExpRegExp는 같지 않으므로 전역 Scope에 혼란이 없습니다. 또한, ncz에 대한 const 선언은 바인딩은 생성하지만 전역 객체에 대한 프로퍼티를 추가하지 않습니다. 이 기능은 전역 객체에 프로퍼티을 생성하고 싶지 않을 때 전역 영역에서 letconst가 훨씬 더 안전하게 할수 있도록 합니다.

만약 전역 객체에서 사용할 수 있어야 하는 코드가 필요하다면 전역 Scope에서 var을 여전히 사용할 수 있습니다. 이런 방법은 일반적으로 프레임이나 윈도우에서 코드에 액세스하는 경우에 일반적으로 사용합니다.

Block-Binding에 대한 새로운 Best Practices

ECMAScript 6가 개발 중에 있는 동안, 변수 선언에 var 대신에 let을 디폴트로 사용해야 한다는 생각이 널리 퍼졌습니다. 많은 JavaScript 개발자들은 letvar의 실행 방식과 똑같이 실행된다고 생각했습니다. 그래서 선언자 키워드의 1:1 대체(Direct replacement)가 논리적으로 합당하게 생각되었습니다. 그리고 값의 수정에 대한 보호가 필요한 변수에는 const를 사용하도록 생각했습니다.

하지만 더많은 개발자가 ECMAScript 6으로 마이그레이션할 때에는 대체 접근법(Alternative approach)이 인기를 얻었습니다. 기본적으로 const를 사용하고 변수 값을 변경해야 한다는 것을 알때 let을 사용하십시오. 이유는 예상치 못한 값 변경이 버그의 원인이기 되기 때문에 초기화 후 대부분의 변수가 값을 변경해서는 안된다는 것입니다. 이 아이디어는 많은 공감을 얻었으면 ECMAScript 6을 채택할 때 고려할만한 가치가 있습니다.

요약

letconst Block-binding은 JavaScript에 Lexical scoping을 도입합니다. 이러한 선언은 Hoisting되지 않으며 선언된 블록내에만 존재합니다. 변수가 다른 언어와 더 유사하고 의도하지 않은 오류가 발생할 가능성이 적은 동작을 제공하므로 변수가 필요한 위치에 정확히 선언될 수 있습니다. 부작용으로 typeof와 같이 안전한 연산자를 사용하더라도 변수가 선언되기 전에 변수에 액세스할 수 없습니다. 선언전에 Block-binding에 액세스하려고 하면 TDZ(Temporary Dead Zone) 바인딩의 존재로 인해 오류가 발생합니다.

대부분의 경우 letconstvar와 비슷한 방식으로 동작합니다. 하지만 for loop에서는 그렇지 않습니다. letconst 둘다에 대해서, for-infor-of loop는 반복할 때마다 새로운 바인딩을 생성합니다. 즉, loop 본문 내부에서 작성된 함수는 loop의 최종 반복 이후(loop를 벗어난 이후)가 아닌 현재 반복중인 loop 내부에서만 액세스할 수 있음을 의미합니다. for loop에서 let 선언에 대해서도 마찬가지입니다. 반면에 for loop에서 const 선언을 사용하려고 하면 에러가 발생할 수 있습니다.

Block-binding을 위한 현재의 최선의 관행은 디폴트로 const를 사용하고 변수의 값을 변경할 필요가 있을 때만 let을 사용하는 것입니다. 이렇게하면 특정 유형의 오류를 방지하는데 도움이되고 코드의 기본 무결성이 보장됩니다.


이 내용은 나중에 참고하기 위해 제가 공부하며 정리한 내용입니다.
의역, 오역, 직역이 있을 수 있음을 알려드립니다.
This post is a translation of this original article [https://leanpub.com/understandinges6/read#leanpub-auto-block-bindings]

참고

공유하기