[JS] var, let, const 뜯어보기

Wol-dan·2021년 9월 14일
3

자바스크립트

목록 보기
1/6
post-thumbnail
post-custom-banner

이 글의 목표 🤔

다음 질문에 대해 고민해보기 위해서 이 글을 작성하였습니다.

  • 자바스크립트에서 변수를 선언할 때 var를 쓰는 것을 지양하라고 하는데, 그 이유가 무엇일까?
  • var 대신 사용하라고 하는 ES6의 let, constvar와 무엇이 다를까?
  • letconst의 차이점과 let보다 const를 쓰라고 하는 이유는 무엇일까?

var의 문제점

  1. 개발자의 실수를 유도하는 var

var는 좋게 말해 유연하다고 할 수도 있지만, 다음과 같은 특징 때문에 개발자의 실수를 유도한다.

  • var 키워드 없이 변수를 선언할 수 있다. -> 전역 변수 생성을 남발할 수 있다.
  • 변수의 중복 선언(재선언)이 가능하다. -> 의도치 않게 변수값 변경이 일어날 수 있다.
// var 키워드 생략 가능
num = 1;

// 변수의 중복 선언 가능
var hobby = 'running';
var hobby = 'singing';

let color = 'blue';
let color = 'purple'; // Uncaught SyntaxError. var와 다르게 let, const는 중복 선언이 불가능하다.
  1. 선언 전에 변수를 참조할 수 있다.

var로 선언한 변수는 선언 전에도 참조할 수 있다는 문제점이 있다. 이게 무슨 말이냐면, 아직 변수를 선언하기 전에 그 변수를 참조했을 때 어떤 에러도 발생시키지 않는다는 것이다.

-> 이 현상이 발생하는 이유는 호이스팅(문서 업로드 예정) 때문이다. 선언 전에 참조가 가능한 것은 프로그램의 흐름상 맞지 않고 가독성을 떨어뜨리며 예상치 못한 버그를 일으킬 수 있기 때문에 문제가 있다.

console.log(x); // undefined. x를 선언하기 전인데도 참조가 가능하다.
var x;

console.log(y); // ReferenceError: y is not defined. 반면 let으로 선언한 변수를 선언 전에 참조하면 에러가 발생한다.
let y;
  1. 함수 스코프(Function Level Scope)
  • 함수 스코프(Function Level Scope): 함수 단위로 스코프를 지정한다. 함수 스코프를 따르는 경우, 함수 내에서 선언된 변수는 함수 내에서만 유효하고, 함수 외부에서 접근할 수 없다.
  • 블록 스코프(Block Level Scope): 블록(중괄호 {}) 단위로 스코프를 지정한다. 함수 블록을 포함해 if, for, while등이 모두 블록에 포함된다. 블록 내에 선언된 변수는 블록 내에서 유효하다.

var 키워드로 생성한 변수는 함수 스코프를 따른다. 그래서 함수 외부에서 생성한 var 변수의 경우 전역 변수가 되어 전역 변수 생성을 남발할 수 있다. 아래 예제를 보자.

var myList = [];
for (var i = 0; i < 5; i++) {
  myList.push(function () {
    console.log(i);
  });
}

myList[0](); // 5
myList[3](); // 5

일단 for문에서 선언한 ifor문 안에서만 사용가능한 지역변수가 아닌 전역 변수가 된다. for문은 함수가 아니고, var로 선언한 변수는 함수 스코프를 따르기 때문에 i는 전역에서 선언된거나 마찬가지이다.

for문을 돌면서 myList라는 배열에 함수를 추가하고 있다. 실행 결과를 예상해보자. 0, 1, 2, 3, 4를 출력하는 함수가 배열에 담긴걸까? myList 배열의 요소를 모두 실행해보면 모두 하나같이 5가 출력된다. 배열의 원소로 넘겨줬던 함수들은 호출될 때 for문이 끝난 후 값이 5가 된 전역 변수 i를 참조하기 때문이다. (우리가 예상한 결과를 출력하기 위해서는 varlet으로 바꿔줘야한다.)

let과 const

변수를 선언하는 키워드인 let, constvar의 문제점을 보완하기 위해 ES6에서 새로 추가되었다. let, const 둘의 공통점을 먼저 알아보자.

  • 변수를 중복 선언할 수 있다.(재선언 가능)
  • 블록 스코프(Block Level Scope)를 따른다.
  • var와 달리 선언하기 전에 참조하면 에러가 발생한다.
let foo = 123;

// 블록 스코프가 생성된다.
{
  let foo = 456; // 지역 변수 👈 전역에서 선언된 변수 foo와는 다른 별개의 변수이다.
  let bar = 456; // 지역 변수
}
console.log(foo); // 123
console.log(bar); // ReferenceError: bar is not defined 👈 전역에서는 코드 블록 내에 선언된 bar 변수를 참조할 수 없다. let으로 선언된 변수는 블록 레벨 스코프이기 때문이다.

💡 let은 전역 스코프에서 선언되더라도 window 객체(전역 객체, 브라우저의 최상위 객체)에 담기지 않는다.

let

  • 재할당이 가능하다.
  • 변수 선언시 초기화 과정을 생략하면 undefined가 자동으로 할당된다.
let x; // undefined.

const

  • 재할당이 불가능하다.
  • 변수 선언과 초기화가 한번에 이루어져야한다.
const x; // 에러. 변수 선언과 초기화가 한번에 되어야 한다.

const y = 10;
y = 20; // 에러. const로 선언된 변수는 재할당이 불가능하다.

왜 let보다 const를 써야할까?

프로그램 내에서 바뀌지 않는 값을 사용할 경우 const는 유용하다. 개발자가 실수로 재할당을 통해 그 값을 변경하는 것을 막아주기 때문이다. 그래서 재할당이 필요없는 변수의 경우 let이 아닌 const로 선언하는 것이 좋다. 또한 const를 사용하면 코드의 가독성과 유지보수성을 높일 수 있다. 아래 예제를 보자.

const TAX_RATE = 0.1; // 변수명을 대문자로 선언해 상수임을 명확히 한다.

let preTaxPrice = 100; // 세전 가격

let afterTaxPrice = preTaxPrice + preTaxPrice * TAX_RATE; // 세후 가격

console.log(afterTaxPrice); // 110

변경할 일이 거의 없는 TAX_RATE라는 값을 const 변수로 선언했다. TAX_RATE에 할당된 원시값(Number는 원시타입이다.)은 변경할 수 없는 값(불변성)이고 const 키워드에 의해 재할당이 금지되므로 할당된 이 숫자를 후에 변경할 수 있는 방법은 없다. 나중에 세율(TAX_RATE)자체가 변경되어야하면 상수의 숫자 0.1만 변경해주면 되기 때문에 유지보수성이 좋다.

💡 상수란?

재할당이 금지된 변수를 의미한다.(단 한번만 할당할 수 있는 변수) 상수≠변경 불가능한 값 (둘은 같은 의미가 아니다.)

상수 자세히 알아보기(문서 업로드 예정)

💡 원시 타입, 리터럴(Literal), 불변성(Immutable)

원시 타입, 리터럴, 불변성 자세히 알아보기(문서 업로드 예정)

const 변수에 재할당을 못한다고 해서 객체에 있는 값까지 바꿀 수 없다는 건 아니다. 아래 코드를 보자.

// const 변수에 원시 값을 할당한 경우 값을 변경할 수 없지만, 객체를 할당한 경우에는 값을 변경할 수 있다.
const person = {
  name: 'Lee',
};

person.name = 'Kim'; // 객체는 변경 가능한 값이다. 객체는 재할당 없이도 값의 변경이 가능하기 때문이다.

const 키워드는 재할당을 금지할 뿐 불변을 의미하지는 않는다. const로 선언한 객체를 변경하는 것이 가능한 이유는, 객체 내부 값이 변경되더라도 변수에 할당된 참조값은 변경되지 않기 때문이다.

Ref

profile
정리하고 모으고 커뮤니케이션하는 걸 좋아하는 새싹 웹 개발자🌱
post-custom-banner

0개의 댓글