이전에 작성한 포스팅인 [JAVASCRIPT] Variables : 변수와 상수 - let, const, var 중
심화에 해당하는 변수 선언과 할당과 var에 대한 내용을 정리한다.
변수
앞서 정의했던 변수(Variable)에 대해 되짚어보자면
변수는 하나의 값을 저장하기 위한 메모리 공간 자체
또는 그 메모리 공간을 식별하기 위해 붙이는 고유한 이름(식별자)이다.
var result = 30;
예를 들어 위와 같이 result라는 변수가 있다고 하면
변수의 식별자는 result이고,
이 식별자는 30이라는 값이 저장되어 있는 메모리 공간 0x0669F913을 기억하고 있는 것이다.
이런 변수의 선언과 할당 과정을 자세하게 알아보자.
변수 선언
변수 선언(Variable Declaration)이란
- 값을 저장하기 위한 메모리 공간을 확보하고
- 식별자와 확보된 메모리 공간의 주소를 연결해서 (= 네임 바인딩)
값을 저장할 수 있게 준비하는 것이다.
변수를 선언할 때에는 let, const, var 키워드를 사용한다.
지금부터는 ⚠️ var 키워드⚠️ 를 사용하는 경우로 상황을 한정하여 설명을 이어나간다
var score;
var 키워드가 사용된 위 변수 선언문은
변수 score의 값을 저장할 메모리 공간을 하나 확보한다.
그리고 score라는 식별자와 메모리 공간의 주소(ex. 0x000000F2)를 연결해 준다.
위 선언문은 아직 score의 값이 무엇인지 별도의 할당을 하지 않았는데
이때, 자바스크립트 엔진에 의해 undefined라는 값이 암묵적으로 할당되어 초기화된다.
혹여나 확보한 메모리 공간 안에 이전 애플리케이션에서 사용했던 값(garbage value)이 남아있을 수도 있기 때문이다. 이 위험으로부터 방어하기 위해 undefined로 초기화를 진행해 주는 것이다.
var score;
console.log(score); // ? undefined
자바스크립트 엔진은 변수 선언을 2단계에 거쳐 수행하는데, var에서 이 두 단계는 동시에 진행된다.
- 선언 단계 : 식별자를 실행 컨텍스트에 등록해서 자바스크립트 엔진에 변수의 존재를 알린다.
- 초기화 단계 : 값을 저장하기 위한 메모리 공간을 확보하고 암묵적으로 undefined를 할당해 초기화한다.
실행 컨텍스트
실행 컨텍스트(execution context)는 자바스크립트 엔진이
소스코드를 평가하고 실행하기 위해 필요한 환경을 제공하고
코드의 실행결과를 실제로 관리하는 영역이다.
자바스크립트 엔진은 실행 컨텍스트를 통해서 식별자와 스코프를 관리한다.
위에서는 단순하게 변수 선언 단계에서 자바스크립트 엔진이 변수를 관리할 수 있도록
변수의 존재를 알린다는 정도로 이해하면 된다.
정확한 표현은 변수는 '선언 단계에서 스코프(실행 컨텍스트의 렉시컬 환경)에 변수 식별자를 등록해 자바스크립트 엔진에 변수의 존재를 알린다'이다.
변수 선언의 실행 시점과 변수 호이스팅
자바스크립트 코드는 런타임에 위에서부터 한 줄씩 순차적으로 실행된다.
console.log("hello");
console.log("hi");
console.log("하이");
// ? hello
// ? hi
// ? 하이
그런데 이는 일반적인 내용일 뿐.
여기에 해당되지 않는 예외가 존재하는데 그것이 변수 선언이다.
변수 선언은 소스코드가 한 줄씩 순차적으로 실행되는 시점보다 우선적으로.
소스코드 어디에 있든 상관없이 런타임 이전 단계에서 다른 코드보다 먼저 실행된다.
런타임에 앞서 소스코드를 실행하기 위한 준비 과정으로 평가를 진행하는데
이때 변수 선언을 포함한 모든 선언문을 찾아내어 먼저 실행해 버린다.
이 모습이 코드의 선두로 끌어올려진 것처럼 동작한다고 하여
변수 호이스팅(variable hoisting)이라고 칭한다.
이 증거가 아래 코드이다.
console.log(score); // ? undefined
var score;
score의 선언문 위에 출력문이 있으니 실행되지 않아야 할 것처럼 보이지만
실제로는 score가 변수 호이스팅되어 undefined로 초기화를 먼저 진행하기 때문에
에러 없이 undefined를 무사히 출력해 낸다.
let, const와 변수 호이스팅
var에서 일으키는 여러 문제들로 인하여 ES6에서 도입된 변수 선언 키워드 let과 const
let과 const로 선언된 변수(상수)는 선언하기 전에 사용할 수 없다.
console.log(score);
let score = 80;
// 🚨 ReferenceError: Cannot access 'score' before initialization
선언 이전에 출력을 시도하면
변수에 접근을 하지 못했음을 알리는 참조 에러(Reference Error)가 발생한다.
그렇다고 변수 호이스팅되지 않는 것은 아니다.
변수 호이스팅이 발생하지 않는 것처럼 동작하는 것일 뿐이다.
자바스크립트 엔진은 변수 선언을 선언 단계와 초기화 단계를 거치는데
var는 이 두 단계를 한 번에 진행하지만 let, const는 두 단계를 분리하여 진행한다.
변수 호이스팅으로 인해 선언 단계는 런타임 이전에 실행되지만
초기화 단계는 변수 선언문( let score; )에서 실행된다.
이 두 단계의 사이 변수를 참조할 수 없는 구간을
일시적 사각지대(Temporal Dead Zone, TDZ)라고 부른다.
// (2) 초기화 단계는 미진행 : TDZ
console.log(score); // 🚨 ReferenceError: Cannot access 'score' before initialization
let score; // (1) 런타임 이전에 선언 단계 실행 // (3) 선언문에 도달하여 초기화 단계 실행
console.log(score); // ? undefined
score = 80;
console.log(score); // ? 80
값의 할당
변수에 값을 지정해 주는 것. 이를 할당(assignment)이라고 표현한다.
표현할 때에는 할당 연산자 = 를 사용한다.
var score; // 변수 선언
score = 80; // 값의 할당
이를 한 줄로 단축 표현 할 수 있다. 다만 단축 '표현'일뿐이라 선언/할당은 분리되어 각각 실행된다.
var score = 80; // 변수 선언과 값의 할당
값의 할당은 소스코드를 순차적으로 실행시키는 런타임에 실행된다.
즉 선언만 런타임 전에 우선적으로 실행될 뿐, 나머지 소스코드는 위에서부터 순차적으로 진행된다.
console.log(score); // (2) ? undefined
var score; // (1) 변수 선언
score = 80; // (3) 값의 할당
console.log(score); // (4) ? 80
그래서 위 코드의 경우
var score; 가 우선 실행되어 undefined로 초기화된 상태에서 첫 번째 출력문이
scroe = 80; 으로 값이 80으로 할당된 이후에 두 번째 출력문이 실행된다.
당연히 선언문과 할당문의 순서를 뒤집어도 결과는 동일하다.
console.log(score); // (2) ? undefined
score = 80; // (3) 값의 할당
var score; // (1) 변수 선언
console.log(score); // (4) ? 80
값의 재할당
이미 값이 할당되어 있는 변수에 새로운 값을 또다시 할당하는 것을 재할당이라고 한다.
재할당할 수 없는 변수에 대해서는 우린 이를 변수라 부르지 않고 상수라고 부른다.
사실 앞서 봤듯이 선언과 동시에 undefined로 초기화 하고나서 할당을 하기 때문에
엄밀하게 말하자면 첫 번째 할당도 재할당인 셈이다.
var score; // 변수 선언 (엄밀히 말하면 undefined 할당)
score = 80; // 값의 할당 (엄밀히 말하면 재할당)
score = 90; // 값의 재할당
재할당을 메모리 측면에서 확인해 보자.
새로운 값은 어느 곳에, 이전 값은 어떻게 처리해야 할까.
❌ 연결된 메모리 공간에 이전 값을 지우고 새로운 값을 저장한다
⭕ 새로운 메모리 공간을 확보해 새로운 값을 저장하고 연결한다
변수를 선언하면서 연결했던 메모리 공간의 값을 변경해 주는 것이 아니라
이전 메모리 공간은 내버려 두고 새로운 메모리 공간을 확보,
그 메모리 공간에 새로 할당된 숫자 값을 저장하고 식별자와 연결해 준다.
score 변수의 이전 값 undefined와 80은 이제 어느 식별자와도 연결되어 있지 않다.
이런 불필요한 값들은 가비지 콜렉터에 의해 메모리에서 자동 해제된다.
단, 메모리에서 언제 해제되는지는 예측할 수 없다.
가비지 콜렉터
가비지 콜렉터(garbage collector)는 애플리케이션이 할당한 메모리 공간을 주기적으로 검사하여
더 이상 사용되지 않는 메모리(어떤 식별자도 참조하지 않는 메모리 공간)를 해제하는 기능을 말한다.
자바스크립트는 메모리 관리 기능을 언어 차원에서 담당하는 매니지드 언어(managed language)이기 때문에
이 기능을 통해 언어 자체적으로 메모리 누수를 방지한다.
참고
- [책] 모던 자바스크립트 Deep Dive
- 04_변수
- 15_let, const 키워드와 블록 레벨 스코프
'JAVASCRIPT' 카테고리의 다른 글
[JAVASCRIPT][PCCP 대비] 프로그래머스 레벨1, 2 빠르게 풀고 메모하기 (0) | 2024.05.08 |
---|---|
[JAVASCRIPT] Execution Context : 실행 컨텍스트 - 콜스택 / 렉시컬 환경 / 호이스팅 / 스코프 체인 (0) | 2024.01.28 |
[JAVASCRIPT] Callback Function : 콜백 함수 - 타이머 함수 / 비동기 / Promise / async - await (0) | 2023.08.27 |
[JAVASCRIPT] Function : 함수 (0) | 2023.08.27 |
[JAVASCRIPT] Data Types : 자료형 - primitive type (0) | 2023.07.18 |