[Frontend 기술 면접 대비] var, let, const

Jo HangJoon·2022년 7월 11일
0

[Frontend 기술 면접 대비] 시리즈는 Frontend 개발자로 취업하기 위해 내 프로젝트 경험과 지식들을 정리한 내용이다.


질문: var, let, const의 차이는?


1. 변수

변수(variable)란 하나의 값을 저장하기 위해 확보한 메모리 공간 자체 또는 그 메모리 공간을 식별하기 위해 붙인 이름이다.

  • 변수 선언(declaration): 변수를 실행 context의 변수 객체에 등록한다. 이 변수 객체는 스코프가 참조하는 대상이 된다.
  • 변수 초기화(Initialization): 변수 객체에 등록된 변수를 위한 공간을 메모리에 확보한다. 이 단계에서 변수는 undefined로 초기화된다.
  • 변수 할당(assignment): undefined로 초기화된 변수에 실제 값을 할당한다.
  • 변수 참조(reference): 변수에 저장된 값을 불러온다.

2. 변수 선언

JavaScript에서 변수 선언은 var, let, const로 할 수 있다.

var

  • ES5까지 유일한 변수 선언 방식.
  • var로 선언한 변수는 동일한 이름으로 여러 번 중복해서 선언이 가능하다.
var name = 'javascript';
console.log(name); // javascript

var name = 'react';
console.log(name); // react
  • 기존에 선언해둔 값이 재할당될 수 있는 문제가 있다. 따라서, 이를 보완하기 위해 letconst가 추가되었다.

let

  • ES6에서 추가된 변수 선언 방식.
  • 중복 선언은 불가능하지만, 재할당은 가능하다.
let name = 'javascript';
console.log(name); // javascript

let name = 'react';
console.log(name);
// Uncaught SyntaxError: Identifier 'name' has already been declared

name = 'vue';
console.log(name); // vue

const

  • ES6에서 추가된 변수 선언 방식.
  • 중복 선언과 재할당 모두 불가능하다. 즉, immutable 하다.
const name = 'javascript';
console.log(name); // javascript

const name = 'react';
console.log(name);
// Uncaught SyntaxError: Identifier 'name' has already been declared

name = 'vue';
console.log(name);
// Uncaught TypeError: Assignment to constant variable

3. 스코프(Scope)

대부분의 프로그래밍 언어는 블록레벨 스코프를 따르지만 JavaScript는 함수레벨 스코프를 따른다.

  • Block-level Scope: 모든 코드 블록(함수, if문, for문, while문, try/catch문 등) 내에서 선언된 변수는 코드 블록 내에서만 유효하며 코드 블록 외부에서는 참조할 수 없다. 즉, 코드 블록 내에서 선언한 변수는 지역 변수다.
  • Function-level Scope: 함수 내에서 선언된 변수는 함수 내에서만 유효하며 함수 외부에서는 참조할 수 없다. 즉, 함수 내에서 선언한 변수는 지역 변수이며 함수 외부에서 선언한 변수는 전역 변수이다.

var - 함수레벨 스코프

var foo = 123; // 전역 변수

console.log(foo); // 123

{
  var foo = 456; // 전역 변수
}

console.log(foo); // 456

함수 외부에서 선언된 foo는 전역 변수이다. 따라서, 전역에서 선언된 값 123을 재할당된 값 456으로 덮어쓰여진다.

let, const - 블록레벨 스코프

let foo = 123; // 전역 변수

{
  let foo = 456; // 지역 변수
  let bar = 456; // 지역 변수
}

console.log(foo); // 123
console.log(bar); // ReferenceError: bar is not defined

코드 블록에서 선언된 foo는 지역 변수이다. 따라서, 코드 블록에서 선언된 456 값을 가지는 지역 변수 foo123 값을 가지는 전역 변수 foo와 다른 변수이다. 또한 bar도 블록레벨 스코프를 갖는 지역변수이다.


4. 호이스팅(Hoisting)

호이스팅(Hoisting)이란 var 선언문이나 function 선언문 등을 해당 스코프의 선두로 옮긴 것처럼 동작하는 특성이다. JavaScript에서는 ES6에서 도입된 let, const을 포함한 모든 선언을 호이스팅한다.

var - 호이스팅 발생

/* 변수 호이스팅 */
console.log(a); // undefined

var a = 5;
console.log(a); // 5

foo(); // foo

function foo() {
	console.log("foo");
}

변수 a가 선언되기 전에 참조되었음에도 에러가 발생하지 않고 undefined가 출력된다. 이는 코드 실행 전에 JavaScript가 내부에서 미리 변수를 선언하고 undefined로 초기화시켰기 때문이다. 함수 선언문 또한 함수가 선언되기 전에 호출되었음에도 에러가 발생하지 않는다.

let, const - 호이스팅이 발생하지만 다른 방식으로 작동함

/* 변수 호이스팅 */
console.log(a); // ReferenceError: a is not defined

let a = 5;
console.log(a); // 5

/* 함수 호이스팅 */
foo(); // error

var foo = function() {
	console.log("foo");
}

변수 a가 선언되기 전에 참조되면 에러가 발생한다. 이는 호이스팅이 발생하지 않는 것이 아닌, 변수의 선언과 초기화 사이에 일시적으로 변수값을 참조할 수 없는 구간인 일시적 사각지대(TDZ; Temporal Dead Zone)에 빠지기 때문이다.

  • let이나 const, 또는 함수 선언문을 사용해 변수를 선언하는 경우, JavaScript 내부에서 코드 실행 전 변수 선언만 해둘뿐 초기화는 코드 실행 과정에서 변수 선언문을 만났을 때 수행한다. 그렇기 때문에 호이스팅이 발생하기는 하지만 값을 참조할 수 없기 때문에 동작하지 않는 것처럼 보인다.

5. 클로저(Closure)

클로저에 대해 MDN은 다음과 같이 정의했다.

클로저는 함수와 그 함수가 선언됐을 때의 렉시컬 환경(Lexical environment)과의 조합이다.

function outerFunc() {
  var x = 10;
  var innerFunc = function () { console.log(x); };
  innerFunc();
}

outerFunc(); // 10
  • 함수 outerfunc 내에서 내부함수 innerfunc가 선언되고 호출되었다. 이때 내부함수 innerFunc은 외부함수 outerFunc의 내부에서 선언되었기 때문에 변수 x에 접근할 수 있다.
  • 즉, 내부함수 innerFunc는 자신이 속한 렉시컬 스코프(전역 스코프, 함수 outerFunc, 자신의 스코프)를 참조할 수 있다.
    • 렉시컬 스코핑(Lexical Scoping): 스코프는 함수를 호출할 때가 아니라 함수를 어디에 선언하였는지에 따라 결정된다. 함수 innerFunc는 함수 outerFunc의 내부에서 선언되었기 때문에 innerFunc의 상위 스코프는 outerFunc이 된다.
  • 내부 함수가 유효한 상태에서는 외부 함수가 종료되어도 외부함수 실행 컨텍스트 내의 활성 객체(Activation object)는 내부함수에 의해 참조되는 한 유효하며 이는 변수의 복사본이 아닌 실제 변수에 접근하는 것이다.

즉, 클로저는 반환된 내부함수가 선언된 자신이 선언됐을 때의 환경(Lexical environment)인 스코프를 기억하여 자신이 선언됐을 때의 환경(scope) 밖에서 호출되어도 그 환경(scope)에 접근할 수 있는 함수를 의미한다.

  • 클로저에 의해 참조되는 외부함수의 변수(x)를 자유변수(Free variable)라 한다.
  • 클로저가 가장 유용하게 사용되는 상황은 현재 상태를 기억하고 변경된 최신 상태를 유지하는 것이다.
  • 상태 변경이나 가변 데이터를 피하고 불변성을 지향하는 함수형 프로그래밍에서 부수 효과(Side Effect)를 최대한 억제하여 오류를 피하고 프로그램의 안정성을 높이기 위해 클로저는 사용된다.

클로저의 장점

  • 데이터를 보존할 수 있다.
  • 정보의 접근을 제한한다.
  • 모듈화에 유리하다.

6. 경험 정리

JavaScript ES6 이후 버전에서는 var은 사용하지 않으며 const를 기본적으로 사용하여 불필요한 변수의 사용의 재사용을 방지하고, 재할당이 필요한 경우 let을 사용하는 것을 권장한다. 실제로, JavaScript를 통해 개발할 당시에 웬만한 변수의 사용은 별 생각없이 const를 사용했다. 하지만 정확히 어떤 의도로 const를 주로 사용하는 지에 대해 몰랐기 때문에 가끔 호이스팅이나 스코프 문제가 생겼었다. 특히, for 반복문처럼 새로운 변수를 선언하고 주로 사용하는 스코프에서 주로 문제가 생겼다.


함께 보면 좋은 글

0개의 댓글