선언자는 자바스크립트에서 가장 기본적인 내용이다. 기본적이지만 핵심적인 내용으로 선언자에 대해 깊이 있는 내용을 알면 자바스크립트의 작동 방식에 대한 이해가 높아진다. 따라서 깊이 있게 공부할 필요성이 있다. 이 글에서는 선언자를 제대로 이해하기 위한 scope에 대한 내용과 var, let, const 각 선언자별로 작동방식과 특징에 대해 알아볼 것이다. 더 나아가 자바스크립트의 메모리 관리 방식에 대해서도 알아본다.
선언자의 기능을 제대로 이해하기 위해 우선 scope 개념에 대한 이해가 필요하다. scope는 변수에 접근할 수 있는 범위를 말한다. 자바스크립트에서 scope은 범위에 따라 크게 function scope과 block scope으로 나뉜다. var, let, const 각각 선언 후 변수가 가지는 scope가 다른데 이것은 밑에 선언자 설명에서 다룬다.
함수 범위 내에서 선언된 변수는 그 함수 안에서만 접근 가능한 범위를 가진다. 이 범위를 function scope이라고 부른다.
아래 코드에서 num
은 함수 안에서 선언됐고 function scope를 가진다. 따라서 num
을 출력하면 오류가 나타난다.
코드
function name() {
var num = 35;
}
console.log(num);
결과
block scope는 말 그대로 블록 안에서 선언된 변수는 블록 영역을 접근 가능한 범위로 가진다.
아래 코드에서 num
은 if문 안에서 선언됐고 block scope를 가진다. 따라서 if문 안에서만 사용할 수 있는 것이다.
코드
if (1) {
let num = 1;
}
console.log(num);
결과
같은 변수명을 여러번 선언할 수 있는 선언자이다. 즉 재선언이 가능하다. 또한 function scope를 따른다. 재선언이 가능하다는 문제 때문에 기존에 선언된 변수를 프로그래머가 자기도 모르게 재선언하여 덮어씌울 수 있는 위험성이 있다. 이 외에도 호이스팅 및 function scope 특성 때문에 문제점이 있는 선언자이다. 따라서 ES6이후로는 var선언자는 잘 쓰이지 않는다. 바로 아래에서 var 선언자의 문제점을 다룬다.
다음과 같은 형태로 사용한다.
var 변수명 = 값;
값을 할당하지 않은 상태로도 선언 가능하다.
var name = 'bathingape'
console.log(name) // bathingape
var name = 'javascript'
console.log(name) // javascript
같은 변수를 두번 선언했음에도 오류가 나지 않고 출력되는걸 볼 수 있다. 이는 코드량이 많아지게 되면 변수가 어디에서 어떻게 사용되었는지 파악하기 힘들고 선언된 값을 의도치 않게 바뀌게 하여 혼란을 초래한다.
for (var i = 0; i < 5; i++) {
console.log(i);
}
console.log('last:', i);
// 0
// 1
// 2
// 3
// 4
// last: 5
위와 같이 for
문이 끝난 이후에도 for
문 안에 선언했던 i변수 값을 읽을 수 있다. if
문 안에서 선언한 변수를 if
문 밖에서도 읽을 수 있다. 어떤 function에서 사용되었냐를 기준으로 scope이 정해지기 때문이다. 이런 점 때문에 var를 사용하면 코드를 분석하는데 어려움이 생기게 된다.
호이스팅은 변수나 함수를 선언했을 때 그 선언된 함수나 변수가 코드의 최상단에 있는 것처럼 작동하는 것이다. 실제 선언된 코드보다 위의 코드에서 해당 변수나 함수를 호출해도 최상단에서 선언한 것처럼 작동해 문제없이 작동한다. 아래 코드를 보자.
function variable() {
console.log(`n : ${n}`); // n : undefiend
var n = 2;
console.log(`n : ${n}`); // n : 2
}
variable();
var n = 1;
첫번째 출력문을 보면 n
이라는 변수가 선언되기 전인데도 해당 변수가 출력이 되고 undefined
라는 결과가 나온걸 볼 수 있다. 이것은 아래에서 선언한 n
변수가 호이스팅되어 최상단에서 일단 var n;
이라고 선언된 것으로 보기 때문이다. 호이스팅 될 때에는 변수에 값은 할당되지 않은 상태로 호이스팅이 일어난다. 따라서 처음에는 undefined
라고 출력되고 var n = 2;
라고 진짜로 선언된 다음에는 n
에 2라는 값이 할당되어 2가 출력된다.
let은 ES6부터 지원하는 자료형이다. var선언자의 위와 같은 문제점들 때문에 나온 선언자이다. 선언된 블록 안에서만 쓰일 수 있는 블록 스코프를 가지게 된다.
다음과 같은 형태로 사용한다. 값을 할당하지 않은 상태로 선언할 수 있다.
let 변수명 = 값;
let num = 10;
if (num == 10) {
let num = 20;
console.log("num in if : ", num); // num in if : 20
}
console.log(num); // 10
블록 영역이 다르면 같은 변수명으로 재선언할 수 있다. 하지만 위처럼 if문 안에서 선언된 let은 그 블록 안에서만 적용되고 if문 밖에서 접근할 수 없는 것을 볼 수 있다. for문 안에서 let으로 선언된 변수도 for문 밖에서는 사용할 수 없다.
let name = 'bathingape'
console.log(name) // bathingape
let name = 'javascript'
console.log(name) // SyntaxError: Identifier 'name' has already been declared
name
변수를 같은 블록 안에서 재선언하여 오류가 발생했다.
var와 똑같이 호이스팅은 되나 초기화 하기 전 변수에 접근하려 하면 ReferenceError
를 발생시킴.
function variable() {
console.log(`n : ${n}`);
let n = 2;
}
variable(); //ReferenceError: n is not defined
변수n
이 선언되기 전에 console.log
로 호출하여 에러가 났다.
상수 선언자로 한번 선언하여 변수에 값을 지정하면 변수의 값을 바꿀 수 없다. 앞으로 값이 변할 경우가 없는 변수나 변수의 값을 고정하여 바뀔 수 없도록 하고 싶을 때 사용한다.
다음과 같은 형태로 사용하며 값을 할당하지 않고 사용할 수 없다.
const 변수명 = 값;
const n; //SyntaxError: Missing initializer in const declaration
const n = 2;
n = 3; //TypeError: Assignment to constant variable.
위 특징 외에 다른 특징들은 let선언자와 똑같다. block scope을 가지고 같은 블록 안에선 재선언 할 수 없으며 초기화 되기 전에 접근이 금지된다.
변경 가능하다. 배열은 객체이고 const로 선언한 객체는 메모리값(객체가 저장된 공간)만 상수이고 객체안의 내용은 변경이 가능하다. 따라서 const로 선언된 변수는 재선언이나 재할당은 할 수 없지만 push(), pop()을 이용해 배열의 값을 추가하거나 삭제할 수 있다. 이와 관련한 자세한 내용은 자바스크립트가 메모리에 어떻게 저장되는지 알아야 한다. 아래 5-3-1에서 간단히 짚고 넘어가자.
다음과 같은 재할당은 할 수 없다.
// 오류
const arr2 = [];
arr2 = ['하이'];
다음과 같은 값을 추가하는 행위는 가능하다.
// 가능
const arr1 = [];
arr1.push('하이');
자바스크립트에서는 정적 메모리 저장 공간인 스택(Stack) 영역과 동적 메모리 저장 공간인 힙(Heap)영역이 있다. 일반적인 변수 선언은 데이터 크기가 유한하고 정적이기 때문에 스택 영역에 저장되고 배열과 오브젝트 같은 객체는 데이터 크기가 동적으로 변경이 가능하기 때문에 힙 영역에 저장된다. 일반적인 변수같이 스택 영역에 데이터가 저장되는 것들은 값이 특정 메모리 주소의 스택 영역에 그대로 저장되지만 힙 역에 값이 저장되는 객체들은 스택 영역에는 값이 저장된 힙 영역의 메모리 주소가 저장되어 있다.
그림에서처럼 객체들은 스택 영역에 힙 영역의 메모리 주소가 저장되므로 const
로 객체 선언을 하게 되면 스택 영역에 저장된 메모리 주소값이 고정되는 것이지 힙 영역에 저장된 값 자체는 고정되지 않는다. 따라서 const
로 배열을 선언해도 배열 안의 값을 변경하거나 push(), pop()으로 값을 추가하거나 삭제하여도 문제가 되지 않는다. 하지만 재선언이나 재할당을 할 수는 없다. 재선언과 재할당은 해당 변수에 새로운 메모리 주소를 부여하는데 메모리 주소는 const
로 선언하여 변수에 메모리 주소값이 고정되었기 때문이다.
자바스크립트 선언자는 기본적이며 중요하다. 선언자 공부를 통해서 선언자의 기본적인 기능 뿐만 아니라 자바스크립트 자체에 대한 이해도를 높이는게 좋을 것 같다. 이번 글에서 기능과 동작 방식에 대해 기본적으로 다뤄야 할 내용은 다 정리해놨다고 생각한다. 좋은 참고글이 되기를 바란다.