
변수의 생명주기
변수도 생명주기를 가진다.
- 변수는 선언에 의해 생성, 할당에 의해 값을 가짐, 그리고 언젠가는 소멸한다.
- 변수에 생명주기가 없다면, 한 번 선언된 변수는 프로그램을 종료하지 않는 한 영원히 메모리 공간을 점유하게 된다.
지역 변수의 생명주기
지역변수의 생명주기는 함수의 생명주기와 일치
- 즉, 함수가 호출되어 실행하는 동안에만 유효
function foo() {
var x = 'local'; // -> 변수 생성, 값 할당
console.log(x); // local
return x; // -> 변수 x 소멸
}
foo();
console.log(x); // ReferenceError: x is not defined
"변수 선언의 실행 시점과 변수 호이스팅"에서 살펴보았듯이, 변수 선언은 선언문이 어디에 있든 상관없이 가장 먼저 실행된다.
다시 말해, 변수 선언은 코드가 한 줄씩 순차적으로 실행되는 시점인 런타임에 실행되는 것이 아니라, 런타임 이전 단계에서 JS 엔진에 의해 먼저 실행된다.
하지만, 엄밀히 말하자면 위 설명은 전역변수에 한정된 것이다.
함수 내부에서 선언한 변수는 함수 호출 직후에 함수 몸체의 코드가 한 줄씩 순차적으로 실행되기 이전에 JS 엔진에 의해 먼저 실행된다.
함수 내부에서 선언된 지역 변수의 생명주기는 함수의 생명주기와 대부분 일치하지만, 지역 변수가 함수보다 오래 생존하는 경우도 있다.
- 변수는 하나의 값을 저장하기 위해 확보한 메모리 공간 자체, 메모리 공간을 식별하기 위해 붙인 이름
- 변수의 생명주기는 메모리 공간이 확보된 시점부터 메모리 공간이 해제되어 가용 메모리풀에 반환되는 시점까지
- 함수 내부에서 선언된 지역변수는 함수가 생성한 스코프에 등록된다.
- 함수가 생성한 스코프는 렉시컬 환경이라 부르는 물리적인 실체가 존재한다고 했다.
- 따라서 변수는 자신이 등록된 스코프가 소멸될 때까지 유효하다.
- 할당된 메모리 공간은 더 이상 그 누구도 참조하지 않을 때 가비지 콜렉터에 의해 해제되어 가용 메모리 풀에 반환된다.
- 즉, 누군가가 메모리 공간을 참조하고 있으면 해제되지 않고 확보된 상태로 남아있게 된다.
- 스코프도 마찬가지다. 누군가 스코프를 참조하고 있으면, 스코프는 소멸하지않고 생존하게 된다. 이를 클로저라고 한다.
지역 변수 호이스팅 스코프
지역 변수 호이스팅은 지역변수 선언이 지역 스코프의 선두로 끌어올려진 것처럼 동작
var x = 'global';
function foo() {
console.log(x); // undefined
var x = 'local';
}
foo();
console.log(x); // 'global'
foo 내부에 선언된 x 변수는 foo 함수 호출 시 이미 undefined로 초기화가 되어 있다.(변수 선언과정은 끝났고 값 할당은 X)
따라서, console 문을 만나는 시점에는 아직 x에 값이 할당되기 이전이기 때문에 undefined 출력
중요한 것은, 최상단에 전역 변수 x까지는 스코프가 유효하지 않다는 것이다.
즉, 함수 내부에서 선언된 지역 변수는 함수 내에서만 스코프를 가지는 것이다.
전역 변수의 생명주기
var 키워드로 선언한 전역 변수의 생명 주기는 전역 객체의 생명 주기와 일치한다.
함수와 달리 전역 코드는 명시적인 호출 없이 실행된다. 다시 말해, 전역 코드는 함수 호출과 같이 전역 코드를 실행하는 특별한 진입점이 없고 코드가 로드되자마자 곧바로 해석되고 실행된다.
var 키워드로 선언한 전역 변수는 전역 객체의 프로퍼티가 된다. -> 이는 전역 변수 생명 주기가 전역 객체 생명 주기와 일치하다는 것
전역 객체(global object)
전역 객체란 코드가 실행되기 이전 단계(즉, 런타임 이전)에 JS 엔진에 의해 어떤 객체보다도 먼저 생성되는 특수한 객체.
클라이언트 사이드 환경(브라우저)에서는 window, 서버 사이드 환경(Node.js)에서는 global 객체를 의미한다.
환경에 따라 전역 객체를 가리키는 다양한 식별자(window, self, this, global)가 존재했으나 ES11에서 globalThis로 통일되었다.
전역 객체는 아래 리스트들을 프로퍼티로 가진다.
- 표준 빌트인 객체(Object, String, Number, Function, Array)
- 환경에 따른 호스트 객체(클라이언트 Web API, Node.js의 호스트 API)
- var 키워드로 선언한 전역 변수와 전역 함수
브라우저에서 전역 객체는 window이므로, 브라우저에서 환경에서 var 키워드로 선언한 전역 변수는 전역 객체 window의 프로퍼티이다.
전역 객체 window는 웹페이지를 닫기 전까지 유효하다.
따라서 브라우저 환경에서 var 키워드로 선언한 전역 변수는 웹페이지를 닫을 때까지 유효하다.
즉, var 키워드로 선언한 전역 변수의 생명 주기는 전역 객체의 생명 주기와 일치한다.

전역 변수의 문제점
- 암묵적 결합
- 전역 변수를 사용한 의도는 전역, 즉 코드 어디서든 참조하고 할당할 수 있는 변수를 사용하겠다는 것
- 이는 모든 코드가 전역 변수를 참조하고 변경할 수 있는 암묵적 결합을 허용하는 것
- 변수의 유효범위가 크다 -> 코드의 가독성은 나빠지고 의도치 않게 상태가 변경될 수 있는 위험도 증가
- 긴 생명주기
- 전역 변수는 생명주기가 길다. -> 따라서 메모리 리소스도 오랜 기간 소비
- var 키워드로 선언한 변수의 경우, 변수의 중복 선언을 허용한다는 것을 의미 -> 변수 이름이 중복된 가능성이 높고 이는 의도치 않은 재할당이 이뤄질 가능성 유발
- 스코프체인 상에서 종점에 존재
- 변수를 검색할 때, 전역 변수가 가장 마지막에 검색된다. -> 즉, 전역 변수의 검색 속도가 가장 느리다.
- 검색 속도 차이가 그다지 크지는 않지만, 속도 차이는 분명하게 존재한다.
- 네임스페이스 오염
- JS의 가장 큰 문제 중 하나는, 파일이 분리되어 있다 하더라도 하나의 전역 스코프를 공유한다.
- 다른 파일 내에서 동일한 이름의 전역 변수나 전역 함수가 같은 스코프 내에 존재할 경우, 예상치 못한 결과를 초래할 수도 있다.
전역 변수 사용을 억제하는 방법
전역 변수를 반드시 사용해야 할 이유를 찾지 못했다면, 지역 변수를 사용해야 한다.
즉, 변수의 스코프는 좁으면 좁을수록 좋다.
1. 즉시 실행 함수 사용
모든 코드를 즉시 실행 함수로 감싸면, 모든 변수는 즉시 실행 함수의 지역 변수가 된다.
즉시 실행 함수 호출이 종료되는 순간, 내부에 있던 모든 변수를 소멸한다. (일회성)
(function () {
var foo = 100; // 즉시 실행 함수의 지역 변수
// ...
})();
console.log(foo); // ReferenceError: foo is not defined
2. 네임스페이스 객체 사용
네임스페이스는 담당할 객체를 생성하고, 전역 변수처럼 사용하고 싶은 변수를 프로퍼티로 추가하는 방법
- 네임스페이스는 객체
- 즉, 네임스페이스 내부에 또 다른 네임스페이스 객체를 프로퍼티로 추가할 수 있다. -> 계층적 네임스페이스
- 네임스페이스 객체 자체가 전역 변수에 할당되므로 그다지 유용하지는 않다.
var MYAPP = {}; // 전역 네임스페이스 객체
MYAPP.name = 'JIOH';
console.log(MYAPP.name); // JIOH
// 네임스페이스 내부에 또 다른 네임스페이스 계층적 구조 생성
MYAPP.person = {
name: 'JIOH2';
age: 100,
};
console.log(MYAPP.person.name); // JIOH
3. 모듈 패턴 사용
클래스를 모방에서 관련이 있는 변수와 함수를 모아 즉시 실행 함수로 감싸 하나의 모듈을 만드는 방법
모듈 패턴을 사용하면 전역 변수의 억제는 물론 캡슐화까지 구현할 수 있다.
- 캡슐화: 객체 상태를 나타내는 프로퍼티와 프로퍼티를 참조하고 동작할 수 있는 동작인 메서드를 하나로 묶는 것
- 캡슐화는 객체의 특정 프로퍼티나 메서드를 감출 목적으로 사용하기도 하는데, 이를 정보 은닉이라고 한다.
/*
Counter 변수에는 즉시 실행 함수로 생성된 클래스 느낌의 객체가 값으로 할당되어 있다.
반환하는 public한 성질의 메서드들은 외부에서 참조가 가능하다.
반환하지 않고 즉시 실행함수 내부에서 선언되어 있는 변수는 private한 성질을 가져, 외부에서 참조가 불가능하다.
*/
var Counter = (function() {
// private 변수
var num = 0;
// 외부로 공개할 데이터나 메서드 프로퍼티를 추가한 객체를 반환
return {
increase() {
return ++num;
},
decrease() {
return --num;
},
},
})();
console.log(Counter.num); // undefined
console.log(Counter.increase()); // 1
console.log(Counter.increase()); // 2
console.log(Counter.decrease()); // 1
console.log(Counter.decrease()); // 0
위의 예시에서 console.log(Counter.num)이 왜 ReferenceError가 아닌 undefined를 리턴하는지 알아보자.
- ReferenceError는 선언되지 않은 생짜 변수를 호출할 때 발생한다. 하지만, Counter.num은 변수를 찾는게 아니라, Counter라는 객체의 num이라는 속성(프로퍼티)을 찾는 행위이다.
- 변수 참조: console.log(abc); -> abc라는 변수가 어디에도 없네? -> ReferenceError
- 객체 프로퍼티 참조: console.log(Counter.num); -> Counter 객체는 존재하네? 근데 그 안에 num이라는 열쇠(key)가 있나? -> 없네? -> 그럼 undefined 반환
4. ES6 모듈
ES6 모듈은 파일 자체의 독자적인 모듈 스코프를 제공한다.
- 모듈 내에서 var 키워드로 선언한 변수는 더이상 전역 변수가 아니며 window 객체의 프로퍼티도 아니게 된다.
- 모던 브라우저에서는 ES6 모듈을 사용할 수 있다.
'Frontend > Javascript-deepdive' 카테고리의 다른 글
| Javascript - Constructor Function (0) | 2025.12.30 |
|---|---|
| javascript - const&let (0) | 2025.12.26 |
| Javscript - Function (0) | 2025.12.21 |
| Javascript - Primitive Type, Object Type (0) | 2025.12.21 |
| Javascript - Control Statement, Object Literal (0) | 2025.12.17 |