[Frontend 기술 면접 대비] 시리즈는 Frontend 개발자로 취업하기 위해 내 프로젝트 경험과 지식들을 정리한 내용이다.
변수(variable)란 하나의 값을 저장하기 위해 확보한 메모리 공간 자체 또는 그 메모리 공간을 식별하기 위해 붙인 이름이다.
- 변수 선언(declaration): 변수를 실행 context의 변수 객체에 등록한다. 이 변수 객체는 스코프가 참조하는 대상이 된다.
- 변수 초기화(Initialization): 변수 객체에 등록된 변수를 위한 공간을 메모리에 확보한다. 이 단계에서 변수는
undefined
로 초기화된다.- 변수 할당(assignment):
undefined
로 초기화된 변수에 실제 값을 할당한다.- 변수 참조(reference): 변수에 저장된 값을 불러온다.
JavaScript에서 변수 선언은 var
, let
, const
로 할 수 있다.
var
var
로 선언한 변수는 동일한 이름으로 여러 번 중복해서 선언이 가능하다.var name = 'javascript';
console.log(name); // javascript
var name = 'react';
console.log(name); // react
let
과 const
가 추가되었다.let
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
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
대부분의 프로그래밍 언어는 블록레벨 스코프를 따르지만 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
값을 가지는 지역 변수 foo
는 123
값을 가지는 전역 변수 foo
와 다른 변수이다. 또한 bar
도 블록레벨 스코프를 갖는 지역변수이다.
호이스팅(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 내부에서 코드 실행 전 변수 선언만 해둘뿐 초기화는 코드 실행 과정에서 변수 선언문을 만났을 때 수행한다. 그렇기 때문에 호이스팅이 발생하기는 하지만 값을 참조할 수 없기 때문에 동작하지 않는 것처럼 보인다.클로저에 대해 MDN은 다음과 같이 정의했다.
클로저는 함수와 그 함수가 선언됐을 때의 렉시컬 환경(Lexical environment)과의 조합이다.
function outerFunc() {
var x = 10;
var innerFunc = function () { console.log(x); };
innerFunc();
}
outerFunc(); // 10
outerfunc
내에서 내부함수 innerfunc
가 선언되고 호출되었다. 이때 내부함수 innerFunc
은 외부함수 outerFunc
의 내부에서 선언되었기 때문에 변수 x
에 접근할 수 있다.innerFunc
는 자신이 속한 렉시컬 스코프(전역 스코프, 함수 outerFunc
, 자신의 스코프)를 참조할 수 있다.innerFunc
는 함수 outerFunc
의 내부에서 선언되었기 때문에 innerFunc
의 상위 스코프는 outerFunc
이 된다.즉, 클로저는 반환된 내부함수가 선언된 자신이 선언됐을 때의 환경(Lexical environment)인 스코프를 기억하여 자신이 선언됐을 때의 환경(scope) 밖에서 호출되어도 그 환경(scope)에 접근할 수 있는 함수를 의미한다.
x
)를 자유변수(Free variable)라 한다.클로저의 장점
- 데이터를 보존할 수 있다.
- 정보의 접근을 제한한다.
- 모듈화에 유리하다.
JavaScript ES6 이후 버전에서는 var
은 사용하지 않으며 const
를 기본적으로 사용하여 불필요한 변수의 사용의 재사용을 방지하고, 재할당이 필요한 경우 let
을 사용하는 것을 권장한다. 실제로, JavaScript를 통해 개발할 당시에 웬만한 변수의 사용은 별 생각없이 const
를 사용했다. 하지만 정확히 어떤 의도로 const
를 주로 사용하는 지에 대해 몰랐기 때문에 가끔 호이스팅이나 스코프 문제가 생겼었다. 특히, for
반복문처럼 새로운 변수를 선언하고 주로 사용하는 스코프에서 주로 문제가 생겼다.
함께 보면 좋은 글