2019. 8. 12. 10:22ㆍJavas/DESC
var
- 함수 레벨 스코프 (Function-level scope)
let
- 블록 레벨 스코프 (Block-level scope)
1. 함수 VS 블록
함수 레벨 스코프(Function-level scope)
함수 내에서 선언된 변수는 함수 내에서만 유효하며 함수 외부에서는 참조할 수 없다. 즉, 함수 내부에서 선언한 변수는 지역 변수이며 함수 외부에서 선언한 변수는 모두 전역 변수이다.
블록 레벨 스코프(Block-level scope)
모든 코드 블록(함수, if 문, for 문, while 문, try/catch 문 등) 내에서 선언된 변수는 코드 블록 내에서만 유효하며 코드 블록 외부에서는 참조할 수 없다. 즉, 코드 블록 내부에서 선언한 변수는 지역 변수이다.
var foo = 123; // 전역 변수
console.log(foo); // 123
{
var foo = 456; // 전역 변수
}
console.log(foo); // 456
let foo = 123; // 전역 변수
{
let foo = 456; // 지역 변수
let bar = 456; // 지역 변수
}
console.log(foo); // 123
console.log(bar); // ReferenceError: bar is not defined
2. 변수 중복 선언 금지
var 는 동일한 이름을 갖는 변수를 중복해서 선언할 수 있었다.
var foo = 123;
var foo = 456; // 중복 선언 허용
let bar = 123;
let bar = 456; // Uncaught SyntaxError: Identifier 'bar' has already been declared
3. 호이스팅
자바스크립트는 ES6에서 도입된 let, const를 포함하여 모든 선언(var, let, const, function, function*, class)을 호이스팅한다. 호이스팅(Hoisting)이란, var 선언문이나 function 선언문 등을 해당 스코프의 선두로 옮긴 것처럼 동작하는 특성을 말한다.
하지만 var 키워드로 선언된 변수와는 달리 let 키워드로 선언된 변수를 선언문 이전에 참조하면 참조 에러(ReferenceError)가 발생한다. 이는 let 키워드로 선언된 변수는 스코프의 시작에서 변수의 선언까지 일시적 사각지대(Temporal Dead Zone; TDZ)에 빠지기 때문이다.
console.log(foo); // undefined
var foo;
console.log(bar); // Error: Uncaught ReferenceError: bar is not defined
let bar;
// 스코프의 선두에서 선언 단계와 초기화 단계가 실행된다.
// 따라서 변수 선언문 이전에 변수를 참조할 수 있다.
console.log(foo); // undefined
var foo;
console.log(foo); // undefined
foo = 1; // 할당문에서 할당 단계가 실행된다.
console.log(foo); // 1
// 스코프의 선두에서 선언 단계가 실행된다.
// 아직 변수가 초기화(메모리 공간 확보와 undefined로 초기화)되지 않았다.
// 따라서 변수 선언문 이전에 변수를 참조할 수 없다.
console.log(foo); // ReferenceError: foo is not defined
let foo; // 변수 선언문에서 초기화 단계가 실행된다.
console.log(foo); // undefined
foo = 1; // 할당문에서 할당 단계가 실행된다.
console.log(foo); // 1
4. 클로저
var funcs = [];
// 함수의 배열을 생성하는 for 루프의 i는 전역 변수다.
for (var i = 0; i < 3; i++) {
funcs.push(function () { console.log(i); });
}
// 배열에서 함수를 꺼내어 호출한다.
for (var j = 0; j < 3; j++) {
funcs[j]();
}
var funcs = [];
// 함수의 배열을 생성하는 for 루프의 i는 전역 변수다.
for (var i = 0; i < 3; i++) {
(function (index) { // index는 자유변수다.
funcs.push(function () { console.log(index); });
}(i));
}
// 배열에서 함수를 꺼내어 호출한다
for (var j = 0; j < 3; j++) {
funcs[j]();
}
var funcs = [];
// 함수의 배열을 생성하는 for 루프의 i는 for 루프의 코드 블록에서만 유효한 지역 변수이면서 자유 변수이다.
for (let i = 0; i < 3; i++) {
funcs.push(function () { console.log(i); });
}
// 배열에서 함수를 꺼내어 호출한다
for (var j = 0; j < 3; j++) {
console.dir(funcs[j]);
funcs[j]();
}
5. 전역 객체와 let
전역 객체(Global Object)는 모든 객체의 유일한 최상위 객체를 의미하며 일반적으로 Browser-side에서는 window 객체, Server-side(Node.js)에서는 global 객체를 의미한다. var 키워드로 선언된 변수를 전역 변수로 사용하면 전역 객체의 프로퍼티가 된다.
var foo = 123; // 전역변수
console.log(window.foo); // 123
let 키워드로 선언된 변수를 전역 변수로 사용하는 경우, let 전역 변수는 전역 객체의 프로퍼티가 아니다. 즉, window.foo와 같이 접근할 수 없다. let 전역 변수는 보이지 않는 개념적인 블록 내에 존재하게 된다.
let foo = 123; // 전역변수
console.log(window.foo); // undefined
const
const는 상수(변하지 않는 값)를 위해 사용한다. 하지만 반드시 상수만을 위해 사용하지는 않는다. 이에 대해서는 후반부에 설명한다. const의 특징은 let과 대부분 동일하므로 let과 다른 점만 살펴보도록 하자.
1. 선언과 초기화
let은 재할당이 자유로우나 const는 재할당이 금지된다.
const FOO = 123;
FOO = 456; // TypeError: Assignment to constant variable.
주의할 점은 const는 반드시 선언과 동시에 할당이 이루어져야 한다는 것이다. 그렇지 않으면 다음처럼 문법 에러(SyntaxError)가 발생한다.
const FOO; // SyntaxError: Missing initializer in const declaration
또한, const는 let과 마찬가지로 블록 레벨 스코프를 갖는다.
{
const FOO = 10;
console.log(FOO); //10
}
console.log(FOO); // ReferenceError: FOO is not defined
2. 상수
상수는 가독성과 유지보수의 편의를 위해 적극적으로 사용해야 한다. 예를 들어 아래 코드를 살펴보자.
// 10의 의미를 알기 어렵기 때문에 가독성이 좋지 않다.
if (rows > 10) { }
// 값의 의미를 명확히 기술하여 가독성이 향상되었다.
const MAXROWS = 10;
if (rows > MAXROWS) { }
조건문 내의 10은 어떤 의미로 사용하였는지 파악하기가 곤란하다. 하지만 네이밍이 적절한 상수로 선언하면 가독성과 유지보수성이 대폭 향상된다.
const는 객체에도 사용할 수 있다. 물론 이때도 재할당은 금지된다.
const obj = { foo: 123 };
obj = { bar: 456 }; // TypeError: Assignment to constant variable.
3. const와 객체
const는 재할당이 금지된다. 이는 const 변수의 타입이 객체인 경우, 객체에 대한 참조를 변경하지 못한다는 것을 의미한다. 하지만 이때 객체의 프로퍼티는 보호되지 않는다. 다시 말하자면 재할당은 불가능하지만 할당된 객체의 내용(프로퍼티의 추가, 삭제, 프로퍼티 값의 변경)은 변경할 수 있다.
const user = { name: 'Lee' };
// const 변수는 재할당이 금지된다.
// user = {}; // TypeError: Assignment to constant variable.
// 객체의 내용은 변경할 수 있다.
user.name = 'Kim';
console.log(user); // { name: 'Kim' }
객체의 내용이 변경되더라도 객체 타입 변수에 할당된 주소값은 변경되지 않는다. 따라서 객체 타입 변수 선언에는 const를 사용하는 것이 좋다. 만약에 명시적으로 객체 타입 변수의 주소값을 변경(재할당)하여야 한다면 let을 사용한다.
'Javas > DESC' 카테고리의 다른 글
| 연산자 & 제어문 & 타입변환과 단축평가 (0) | 2020.03.19 |
|---|---|
| 기본 문법 & 데이터 타입과 변수 (0) | 2020.03.17 |
| 함수만들기, 함수기본과 매개변수 (0) | 2019.08.09 |
| 클로저 Closures (0) | 2019.07.17 |