14장 전역 변수의 문제점
변수의 생명 주기
- 변수는 생물처럼 생성되고 소멸되는 생명 주기(Life Cycle) 을 가진다.
- 변수에 생명 주기가 없다면 한번 선언된 변수는 프로그램을 종료하지 않는 한 영원히 메모리 공간을 점유하게 된다.
- 변수는 자신이 선언된 위치에서 생성되고 소멸한다.
- 전역 변수의 생명 주기는 애플리케이션의 생명 주기와 같다.
function foo() {
var x = 'local';
console.log(x); // local
return ;
}
foo();
console.log(x); // ReferenceError: x is not defined
- 지역 변수는 foo 함수 내의 변수 선언문이 호출되기 이전까지는 생성되지 않는다.
- 4.4절에서 보았듯이, 변수 선언은 선언문이 어디에 있든 상관없이 가장 먼저 실행된다.
- 그러나 위 설명은 전역 변수에 한정된 것이다.
- foo 함수가 호출되면 다른 문들이 실행되기 이전에 변수 선언문이 실행되면서 변수가 생성되고 undefined로 초기화된다.
- 그리고 함수가 종료하면 x 변수도 소멸되어 생명 주기가 종료된다.
- 즉, 지역 변수의 생명 주기는 함수의 생명 주기와 일치한다.
- 일반적으로 지역 변수의 생명 주기는 함수의 생명 주기와 대부분 일치하지만, 지역 변수가 함수보다 오래 생존하는 경우도 있다.
변수와 메모리
- 변수는 하나의 값을 저장하기 위해 학보된 메모리 공간 자체 또는 그 메모리 공간을 식별하기 위해 붙인 이름이다.
- 따라서 변수의 생명주기는 "메모리 공간이 확보(allocate)된 시점"부터 "메모리 공간이 해제(release)되어 가용 메모리 풀(memory pool)에 반환되는 시점"까지다.
- 함수 내부에서 선언된 지역 변수는 함수가 생성한 스코프에 등록된다.
- 함수가 생성한 스코프는 렉시컬 환경이라 부르는 물리적인 실체가 있다.
- 따라서 변수는 등록된 스코프가 소멸(메모리 해제)될 때까지 유효하다.
- 할당된 메모리 공간은 더 이상 누구도 참조하지 않을 때 가비지 콜렉터에 의해 해제되어 가용 메모리 풀에 반환된다.
- 즉, 누군가 메모리 공간을 참조하고 있으면 해제되지 않고 확보된 상태로 남아있게 된다.
- 즉, 누군가 스코프를 참조하고 있으면 스코프는 소멸하지 않고 생존하게 된다. => 24장 클로저
var x = 'global';
function foo() {
var x
console.log(x); // 이 x는 아래의 지역 변수 x를 가리킨다. 변수 할당문이 실행되기 전까지는 undefined 값을 갖는다.
x = 'local';
}
foo();
console.log(x); // global
- 즉, 호이스팅은 스코프를 단위로 동작한다.
전역 변수의 생명 주기
- 함수와 달리 전역 코드는 명시적인 호출 없이 실행된다.
- 전역 코드는 코드가 로드되자마자 곧바로 해석되고 실행된다.
- 전역 코드에는 반환문을 사용할 수 없으므로 더 이상 실행할 문이 없을 때 종료한다.
var
키워드로 선언한 전역 변수는 전역 객체의 프로퍼티가 된다. 이는 전역 변수의 생명 주기는 전역 객체의 생명 주기와 일치한다는 것이다.- 브라우저 환경에서는
window
가 전역 객체이므로var
키워드로 선언한 전역 변수는window
객체의 프로퍼티이다. 브라우저 환경에서는var
키워드로 선언한 전역 변수는 웹페이지를 닫을 때까지 유효하다.
전역 변수의 문제점
1. 암묵적 결합
- 어디서든 참조하고 할당할 수 있는 변수 => 전역 변수
- 이는 곧 모든 코드가 전역 변수를 참조하고 변경할 수 있는 "암묵적 결합(Implicit Coupling)" 허용
- 변수의 유효 범위가 커질수록, 가독성은 나빠지고 의도치 않은 상태 변경의 위험도도 증가한다.
2. 긴 생명 주기
- 전역 변수는 생명 주기가 길다.
- 따라서 메모리 리소스도 오랫동안 잡아먹는다.
- 특히, var 키워드는 변수의 중복 선언을 허용하므로 생명 주기가 긴 전역 변수는 변수 이름이 중복될 가능성이 있다.
3. 스코프 체인 상에서 종점에 존재
- 전역 변수는 스코프 체인 상에서 종점에 존재한다.
- 즉, 변수를 검색할 때 전역 변수가 가장 마지막에 검색되므로 전역 변수의 검색 속도가 가장 느리다고 말할 수 있다.
4. 네임스페이스 오염
- 자바스크립트에서는 파일이 분리되어도 하나의 전역 스코프를 공유한다.
- 따라서 다른 파일 내에서 동일한 이름으로 명명된 전역 변수나 함수가 같은 스코프 내에 존재할 경우 예상치 못한 결과를 가져올 수 있다.
전역 변수의 사용을 억제하는 방법
전역 변수를 반드시 사용해야 할 이유를 찾지 못한다면 지역 변수를 사용해야 한다. 변수의 스코프는 좁을수록 좋다.
1. 즉시 실행 함수
(function () {
var foo = 10;
// ...
}());
console.log(foo); // ReferenceError: foo is not defined
- 함수 정의와 동시에 호출되는 즉시 실행 함수는 단 한 번만 호출된다.
- 따라서 모든 코드를 즉시 실행 함수로 감싸면 모든 변수는 즉시 실행 함수의 지역 변수가 된다.
- 라이브러리 등에 자주 사용된다.
2. 네임스페이스 객체
- 전역 네임스페이스 객체를 만들어 프로퍼티로 관리하는 방법이다. 어차피 전역 객체이기 때문에 그닥 좋은 방법은 아니다.
var MYAPP = {}; // 전역 네임스페이스 객체
MYAPP.name = 'Lee';
console.log(MYAPP.name); // Lee
var MYAPP = {}; // 전역 네임스페이스 객체
MYAPP.person = {
name: 'Lee',
address: 'Seoul'
};
console.log(MYAPP.person.name); // Lee
3. 모듈 패턴
- 클로저를 활용해 클래스를 모방해서 만든 패턴이다.
- 전역 변수의 억제와 캡슐화까지 구현할 수 있다.
var Counter = (function () {
// private 변수
var num = 0;
// 외부로 공개할 데이터나 메서드를 프로퍼티로 추가한 객체를 반환한다.
return {
increase() {
return ++num;
},
decrease() {
return --num;
},
getNum() {
return num;
}
};
}());
// private 변수는 외부로 노출되지 않는다.
console.log(Counter.num); // undefined
console.log(Counter.getNum()); // 0
console.log(Counter.increase()); // 1
console.log(Counter.getNum()); // 1
console.log(Counter.increase()); // 2
console.log(Counter.getNum()); // 2
console.log(Counter.decrease()); // 1
console.log(Counter.decrease()); // 0
4. ES6 모듈
- ES6 모듈을 사용하면 전역 변수를 사용할 수 없다. ES6 모듈은 파일 자체의 독자적인 모듈 스코프를 제공한다.
- 모듈 내에서
var
키워드로 선언하더라도 전역 변수가 아니며window
객체의 프로퍼티도 아니다. script
태그에type="module"
어트리뷰트를 추가하면 로드된 자바스크립트 파일은 모듈로 동작한다.
<script type="module" src="lib.mjs"></script>
<script type="module" src="app.mjs"></script>
'프로젝트 > 정글 FE 스터디' 카테고리의 다른 글
[React] Reacting to input with State (0) | 2023.09.14 |
---|---|
[React] Updating Objects in State (0) | 2023.09.10 |
[React] State: A Component's Memory (1) | 2023.09.03 |
[FE] Deep Dive - 10장 객체 리터럴 (0) | 2023.09.02 |
[React] map에서 key를 사용하는 이유 (1) | 2023.08.31 |