let과 const는 자바스크립트에서 비교적 새로운 변수 선언 방법 입니다. let과 var는 비슷하지만 자바스크립트에서 흔한 “gotchas”https://en.wikipedia.org/wiki/Gotcha_(programming) 를 피할 수 있게 해 줍니다. const는 let에 변수에 재할당을 막도록 확장된 것입니다.
TypeScript는 자바스크립트의 superset으로 let과 const를 지원합니다. 이 두가지 새로운 선언에 대해서 자세히 알아 보고 왜 var보다 더 바람직한지 설명할 것입니다.
var 선언
자바스크립트에서 변수를 선언은 var 키워드로 해왔습니다.
|
|
함수 안에서도 변수를 선언할 수 있습니다.
|
|
그리고 다른 함수에서 이 변수들에 접근할 수 있습니다.
|
|
위 예에서 g는 f에서 선언된 변수 a를 캡처(capture)합니다. g가 호출될때 a의 값은 f안의 a의 값에 묶입니다. f가 실행을 완료했어도 g가 호출되면 a에 접근하고 수정할 수 있습니다.
|
|
Scoping rules
var 선언은 이상한 scoping rules를 가집니다. 다음 예제를 보면:
|
|
변수 x는 if 블럭(block)안에서 선언되었지만 블럭 밖에서도 접근할 수 있습니다. var 선언을 포함하는 블럭과는 관계 없이 그것을 감싸고 있는 함수, 모듈(module), 네임스페이스(namesapce) 또는 global scope의 어디에서나 접근이 가능합니다. 어떤 사람들은 이것을 var-scoping 또는 function-scoping라고 부릅니다. 파라미터(parameter)도 또한 function-scoping을 따릅니다.
이러한 scoping rule은 몇몇 형태의 실수를 유발할 수 있습니다. 그중 하나는 변수를 여러번 선언해도 에러가 아니라는 사실이다.
|
|
어쩌면 몇몇은 찾아내기 쉬울 수도 있습니다. i는 동일한 function-scope 변수이기 때문에 안쪽의 for-루프는 뜻하지 않게 변수 i를 덮어 씁니다.
이런 버그는 코드리뷰에서도 잡기 어렵습니다.
변수 캡처의 이상한 점(variable capturing quirks)
|
|
setTimeout은 일정 시간 후에 함수를 실행합니다.
실행 결과는 이렇습니다.
|
|
많은 자바스크립트 개발자는 이 동작을 잘 알고 있지만, 대부분의 사람들은 출력이 다음과 같을 거라고 기대합니다.
|
|
setTimeout에 전달하는 모든 함수 표현식은 실제로 동일한 범위의 동일한 i를 참조합니다.
setTimeout은 일정 시간 후에 함수를 실행하지만 for 루프가 실행을 중지 한 후에만 실행됩니다. for 루프가 실행을 멈출 때 i의 값은 10입니다. 따라서 주어진 함수가 호출될 때마다 10을 출력합니다.
일반적인 해결 방법은 즉시 호출 함수 표현인 IIFE(Immediately Invoked Function Expression)을 사용해서 각 반복에서 i를 캡처하는 것입니다.
|
|
이 특이한 패턴은 실제로 매우 일반적으로 사용됩니다.
let 선언
지금까지 var 키워드는 몇가지 문제가 있다는 것을 알게 되었고 그것이 let가 도입된 이유입니다.
|
|
Block-scoping
let을 사용해서 변수를 선언하면 lexical-scoping 또는 block-scoping을 사용합니다. 감싸고 있는 함수 전체로 scope를 넓히는 var로 선언된 변수와를 다르게 block-scope 변수는 가장 가까운 포함 블럭 또는 for루프 외부에서는 볼 수 없습니다.
|
|
a와 b는 로컬 변수입니다. a의 scope는 f의 바디로 제한되는데 비해 b의 scope는 if 문의 블럭으로 제한됩니다.
catch절 안에서 선언된 변수도 비슷한 scoping 룰이 적용됩니다.
|
|
블럭 범위 변수의 또 다른 특성은 실제로 선언되기 전에 읽거나 쓸 수 없다는 것입니다.
|
|
주의해야 할 점은 블럭 범위가 선언되기 전에 변수를 캡처할 수 있다는 것입니다. 단, 선언되기 전에 함수를 호출하는 것은 안됩니다. TypeScript에서는 에러가 발생하지는 않지만 ES2015에서는 실행할때 ReferenceError 예외가 발생합니다.
|
|
Re-declarations and Shadowing
var 선언은 동일한 이름으로 여러번 선언해도 에러가 발생하지 않습니다.
|
|
위 예에서 모든 x 선언은 실제로 동일한 x를 참조합니다. 이것은 종종 버그의 원인이 됩니다.let은 다릅니다.
|
|
|
|
블럭을 지정해 주면 동일한 이름으로 변수를 선언할 수 있습니다.
|
|
안쪽 scope에 새로 선언하는 것을 shadowing라고 합니다. 어떤 버그는 막을 수 있지만 또 다른 버그를 만들어 낼 수 있습니다.
예를 들어 sumMatrix 함수를 let으로 다시 작성했다고 생각해 봅시다.
|
|
이 버전은 안쪽 i가 바깥 루프의 i를 가려서 정확하게 합산을 수행할 수 있습니다.
더 명확한 코드를 작성하기 위해 shadowing은 일반적으로 피해야 합니다.
Block-scoped variable capturing
var 선언으로 변수 캡처의 개념을 처음 접했을 때 일단 캡처 된 변수가 어떻게 작동하는지 간략하게 살펴 보았습니다. 이것에 대한 더 나은 직감을주기 위해 스코프가 실행될 때마다 변수의 “환경”을 생성합니다. 해당 환경 및 캡처 된 변수는 범위 내의 모든 항목이 완료된 후에도 존재할 수 있습니다.
|
|
환경안에서 city를 캡처했으므로 if블럭이 실행을 완료했음에도 불구하고 여전히 액세스 할 수 있습니다.
이전의 setTimeout 예제에서는 for 루프를 반복 할 때마다 변수의 상태를 캡처하기 위해 IIFE를 사용해야한다는 결론을 얻었습니다. 사실 우리가 수행 한 작업은 캡처 된 변수에 대한 새로운 변수 환경을 만드는 것이 었습니다. 그것은 약간의 고통 이었지만, 다행스럽게도 TypeScript에서는 다시 할 필요가 없습니다.
루프의 일부로 선언될 때 let 선언은 철저하게 다른게 동작합니다.
루프 자체에 새로운 환경을 도입하기보다는 반복마다 새로운 scope를 만듭니다.
지금까지는 IIFE로 해왔으나 setTimeout 예제를 let 선언을 사용해서 바꿀 수 있습니다.
|
|
|
|
const 선언
|
|
let과 비슷하나 값을 바꿀 수 없습니다. 다시 말해서 동일한 scope 내에서 다시 할당 할 수 없습니다.
이것은 참조하는 값이 불변이라는 생각과 혼동되어서는 안됩니다.
|
|
이를 피하기 위해 특별한 조치를 취하지 않으면 const 변수의 내부 상태가 여전히 수정 가능합니다. 다행히도 TypeScript를 사용하면 객체의 멤버를 읽기 전용으로 지정할 수 있습니다. 인터페이스 장에서 자세히 설명합니다.