JavaScript - let, const

이소라·2022년 11월 1일
0

JavaScript

목록 보기
8/22

let, const 키워드와 블록 레벨 스코프


1. var 키워드로 선언한 변수의 문제점

1.1 var 키워드로 선언한 변수는 같은 스코프 내에서 중복 선언을 허용함

  • var 키워드로 변수를 중복 선언하면 초기화문 유무에 따라 다르게 동작함
    • 초기화가 있는 변수 선언문은 JavaScript 엔진에 의해 var 키워드가 없는 것처럼 동작함
    • 초기화가 없는 변수 선언문은 무시됨
var x = 1;
var y = 1;

// 초기화가 있는 변수 선언문은 var 키워드가 없는 것처럼 동작함
var x = 100;

// 초기화가 없는 변수 선언문은 무시됨
var y;

console.log(x); // 100
console.log(y); // 1

1.2 var 키워드로 선언한 변수는 함수 레벨 스코프를 가짐

  • var 키워드로 선언한 변수는 함수의 코드 블록만을 지역 스코프로 인정함
    • 함수 레벨 스코프는 전역 변수를 남발할 가능성을 높여서, 의도치 않게 전역 변수가 중복 선언되는 경우를 발생시킴
      • 함수 외부의 코드 블록 내에서 var 키워드로 선언한 변수 = 전역 변수
      • 함수 외부의 for 문의 변수 선언문에서 var 키워드로 선언한 변수 = 전역 변수
// 함수 외부의 코드 블록 내에서 var 키워드로 변수 선언할 경우
var x = 1;

if (true) {
  // 코드 블록 내에서 선언한 x는 전역 변수임
  // 이미 선언된 전역 변수 x가 있으므로 중복 선언됨
  var x = 10;
}

// 의도치 않게 x 변수의 값이 변경됨
console.log(x); // 10;
// 함수 외부의 for 문의 변수 선언문에서 var 키워드로 선언할 경우
var i = 10;

// for문에서 선언한 i는 전역 변수임
// 이미 선언된 전역 변수 i가 있으므로 중복 선언됨
for (var i = 0; i < 5; i++) {
  console.log(i); // 0 1 2 3 4
}

// 의도치 않게 i 변수의 값이 변경됨
console.log(i); // 5

1.3 var 키워드로 선언한 변수는 변수 호이스팅에 의해 변수 선언문 이전에 참조할 수 있음

  • var 키워드로 선언한 변수를 변수 선언문 이전에 참조하면 언제나 undefined를 반환함
    • 변수 선언문 이전에 변수를 참조하는 것은 프로그램의 흐름상 맞지 않고 가독성를 떨어뜨리고 오류를 발생시킬 여지를 남김
// 1. 선언 단계 : 변수 호이스팅에 의해 foo 변수가 이미 선언됨
// 2. 초기화 단계 : foo 변수는 undefined로 초기화됨
console.log(foo); // undefined

foo = 123;

console.log(foo); // 123

// 변수 선언은 런타임 이전에 암묵적으로 실행됨
var foo;
  • 위에서 언급한 var 키워드의 단점들을 보완하기 위해, ES6에서는 새로운 변수 선언 키워드인 let과 const를 도입함



2. let 키워드

2.1 let 키워드로 선언한 변수는 같은 스코프 내에서 중복 선언을 금지함

  • let 키워드로 선언한 변수는 같은 스코프 내에서 중복 선언을 허용하지 않음
    • let 키워드로 이름이 같은 변수를 중복 선언하면 문법 에러(SyntaxError)가 발생함
let bar = 123;
let bar = 456; // SyntaxError : Identifier 'bar' has already been declared

2.2 let 키워드로 선언한 변수는 블록 레벨 스코프를 가짐

  • let 키워드로 선언한 변수는 모든 코드 블록(함수, if문, for문, while문, try/catch문 등)을 지역 스코프로 인정하는 블록 레벨 스코프(block level scope)를 따름
// 전역 변수 i
let i = 10;

if (true) {
  // 지역 변수 i, j
  let i = 100;
  let j = 0;
}

console.log(i); // 10
console.log(j); // ReferenceError: j is not defined
  • 함수도 코드 블록이므로 스코프를 만듬
    • 함수 내의 코드 블록은 함수 레벨 스코프에 중첩됨
// 전역 스코프
let i = 10;

function foo() {
  // 함수 레벨 스코프
  let i = 100;
  
  for (let i = 1; i < 3; i++) {
    // 블록 레벨 스코프
    console.log(i); // 1 2
  }
  
  console.log(i); // 100
}

foo();

console.log(i); // 10

2.3 let 키워드로 선언한 변수는 선언 단계와 초기화 단계가 분리되어 진행됨

  • JavaScript 엔진에 의해 암묵적으로 선언 단계가 먼저 실행되지만, 초기화 단계는 변수 선언문에 도달했을 때 실행됨
  • 초기화 단계가 실행되기 이전에 변수에 접근하려고 하면 참조 에러(Reference Error)가 발생함
    • let 키워드로 선언한 변수는 스코프의 시작 지점부터 초기화 시작 지점(변수 선언문)까지 변수를 참조 불가능함
    • 일시적 사각지대(Temporal Dead Zone: TDZ) : 스코프의 시작 지점부터 초기화 시작 지점까지 변수를 참조할 수 없는 구간
// 런타임 이전에 선언 단계가 실행됨, 아직 변수가 초기화되지 않음
console.log(foo); // ReferenceError : foo is not Defined

let foo; // 변수 선언문에서 초기화 단계가 실행됨
console.log(foo); // undefined

foo = 1; // 변수 할당문에서 할당 단계가 실행됨
console.log(foo); // 1
  • let 키워드로 선언한 변수도 변수 호이스팅이 발생함
let foo = 1; // 전역 변수

{
  console.log(foo); // ReferenceError: Cannot access 'foo' before initialization
  let foo = 2; // 지역 변수
}
  • 위 예제에서 let 키워드로 선언한 지역 변수 foo 변수가 변수 호이스팅이 발생하지 않았다면 전역 변수 foo 값을 출력해야 함
    • let 키워드로 선언한 변수도 여전히 호이스팅이 발생하기 때문에, 지역 변수 foo가 초기화 단계 실행 전에 참조되서 참조 에러(Reference Error)가 발생함
    • JavaScript는 모든 선언(var, let, const, function, function*, class 등)을 호이스팅함
      • 단, let, const, class를 사용한 선언문은 초기화 단계(선언문)가 실행되기 전에는 참조가 불가능함

2.4 let 키워드로 선언한 전역 변수는 전역 객체의 프로퍼티가 아님

  • var 키워드로 선언한 전역 변수, 전역 함수, 그리고 선언하지 않은 변수에 값을 할당한 암묵적 전역은 전역 객체 window의 프로퍼티가 됨
    • 전역 객체의 프로터피를 참조할 때 window를 생략 가능함
// 전역 변수
var x = 1;

// 암묵적 전역
y = 2;

// 전역 함수
function foo() {}

console.log(window.x); // 1
console.log(x); // 1

console.log(window.y); // 2
console.log(y); // 2

console.log(window.foo); // f foo() {}
console.log(foo); // f foo() {}
  • let 키워드로 선언한 전역 변수
    • 전역 객체의 프로퍼티가 아님
    • 전역 객체의 프로퍼티로 접근할 수 없음
    • 전역 렉시컬 환경의 선언적 환경 레코드 내에 존재하게 됨
// 전역 변수
let x = 1;

console.log(window.x); // undefined
console.log(x); // 1



3. const 키워드

3.1 const 키워드로 선언한 변수는 반드시 선언과 동시에 초기화해야 함

  • 변수 선언만 할 경우, 문법 에러(Syntax Error)가 발생함
const x = 1;
const y; // SyntaxError: Missing initialier in const declaration
  • const 키워드로 선언한 변수는 블록 레벨 스코프를 가짐
  • const 키워드로 선언한 변수는 변수 호이스팅이 발생하지만, 초기화 단계가 실행하기 이전에 참조하면 참조 에러(Reference Error)가 발생함
{
  // 블록 레벨 스코프
  // 변수 x가 선언되었지만, 아직 초기화되지 않음
  console.log(x); // ReferenceError: Cannot access 'x' before initialization
  const x = 1; // 초기화 단계
  console.log(x); // 1
}

// 전역 스코프
// 블록 레벨 스코프는 하위 스코프이므로 참조 불가능함
console.log(x); // ReferenceError: x is not defined

3.2 const 키워드로 선언한 변수는 재할당이 금지됨

  • const 키워드로 선언한 변수에 재할당할 경우, TypeError가 발생함
const x = 1;
x = 2; // TypeError: Assignment to constant variabe

3.3 const 키워드로 선언한 변수에 할당된 값이 원시 값일 경우, 값을 변경할 수 없음

  • const 키워드로 선언된 변수에 원시 값을 할당한 경우

    • 원시 값은 변경 불가능한 값(immutable value)이고,
    • const 키워드에 의해 재할당이 금지되므로
    • 할당된 값을 변경할 수 없음
  • 상수

    • 재할당이 금지된 변수
    • 상태 유지, 가독성, 유지보수의 편의를 위해 사용을 권장함
    • 일반적으로 상수 이름은 대문자로 선언함
    • 상수 이름이 여러 단어로 이루어진 경우, 스네이크 케이스로 표현하는 것이 일반적임
const TAX_RATE = 0.1;

let preTaxPrice = 100;

let afterTaxPrice = preTaxPrice + (preTaxPrice * TAX_RATE);

3.4 const 키워드로 선언한 변수에 객체를 할당한 경우, 값을 변경할 수 있음

  • const 키워드는 재할당을 금지할뿐 불변을 의미하지 않음
    • 새로운 값을 재할당하는 것은 불가능함
    • 프로퍼티 동적 생성, 삭제, 프로퍼티 값의 변경을 통해 객체를 변경하는 것은 가능함
    • 객체가 변경되더라도 변수에 할당된 참조 값은 변경되지 않음



var vs. let vs. const

  • var, let, const 키워드는 다음과 같이 사용하는 것을 권장함
    1. ES6를 사용하면 var 키워드를 사용하지 않기
    2. 변수 선언에는 기본적으로 const 키워드를 사용하기
      • 의도치 않은 재할당을 방지하기 때문에 안전함
    3. 재할당이 필요한 경우에 한정해 let 키워드를 사용하기
      • 변수의 스코프를 최대한 좁게 만들기

0개의 댓글