변수는 선언
에 의해 생성되고 할당
을 통해 값을 갖는다.
변수는 생성되고 소멸되는 생명 주기
가 있다.
변수는 자신이 선언된 위치에서 생성되고 소멸한다.
그러므로 전역 변수의 생명 주기는 애플리케이션의 생명 주기와 같다.
함수 내부에서 선언된 지역 변수는 함수가 호출되면 생성되고 함수가 종료하면 소멸된다.
function foo() {
var x = 'local'
console.log(x); // local
return x;
}
foo();
console.log(x); // ReferenceError: x is not defined
지역 변수 x
는 foo 함수가 호출되면서 생성되고, foo 함수가 종료될 때 소멸되어 생명 주기가 종료된다.
즉,
함수 내부에서 선언된 지역 변수의 생명 주기는 함수의 생명 주기와 일치한다.
변수
1. 하나의 값을 저장하기 위해 확보한 메모리 공간 자체
2. 그 메모리 공간을 식별하기 위해 붙인 이름
변수의 생명 주기
그 메모리 공간이 확보된 시점부터
메모리 공간이 해제되어 가용 메모리 풀에 반환되는 시점까지이다.
함수 내부에서 선언된 지역 변수는,
함수가 생성한 스코프에 등록된다.
변수는 자신이 등록된 스코프가 소멸(메모리 해제)될 때까지 유효하다.
- 할당된 메모리 공간은 더 이상 누구도 참조하지 않을 때 가비지 콜렉터에 의해 해제되어 가용 메모리 풀에 반환된다. (스코프도 마찬가지)
var x = 'global';
function foo() {
console.log(x); // undefined
var x = 'local';
}
foo();
console.log(x); // global
호이스팅은 스코프를 단위로 동작한다.
전역 변수의 생명 주기 === 전역 객체의 생명 주기
전역 코드
전역 객체
window
, 서버 사이드 환경의 global
객체를 의미전역 객체를 가리키는 식별자로
globalThis
를 쓰자는 내용이 명세에 추가되었다.
Object
, String
, Number
, Function
, Array
...)와 환경에 따른 호스트 객체(클라이언트 Web API, Node.js의 호스트 API), var 키워드로 선언한 전역 변수와 전역 함수를 프로퍼티로 갖는다.var 키워드로 선언한 전역 변수의 생명 주기 === 전역 객체의 생명 주기
- var 키워드로 선언한 전역 변수는 전역 객체
window
의 프로퍼티이다.- 전역 객체
window
는 웹페이지를 닫기 전까지 유효하다.- 브라우저 환경에서 var 키워드로 선언한 전역 변수는 웹페이지를 닫을 때까지 유효하다.
전역 변수는 생명 주기가 길다.
특히 var 키워드는 변수의 중복 선언
을 허용하므로
변수의 스코프는 좁을수록 좋다.
전역 변수를 반드시 사용해야 할 이유가 없다면 지역 변수를 사용해야 한다.
모든 코드를 즉시 실행 함수로 감싸서 모든 변수는 즉시 실행 함수의 지역 변수로 만든다.
선언과 동시에 초기화되므로 플러그인, 라이브러리를 만들 때 많이 사용하며
모듈 패턴의 기반이 되기도 한다.
(function () {
var foo = 10; // 즉시 실행 함수의 지역 변수
...
}());
console.log(foo); // ReferenceError: foo is not defined
즉시 객체 초기화도 비슷하게 동작한다.
({ a: 600, b: 400, util: function () { console.log(this.a + this.b); }, init: function () { console.log(this.util()); } }).init();
메모리 낭비가 일어날 수 있다.
JavaScript는 할당 없이 정의만 할 경우, 전역 네임스페이스는 건드리지 않고
전역 실행 컨텍스트(EC: Execution Context)의 temp=[]
내에 key-value
를 추가하게 된다.
이 EC.temp
영역은 개발자가 접근할 수 없는 영역
이므로 스크립트 내의 다른 영역은 물론 어디에서도 접근할 수 없어 소스코드의 신뢰성에는 큰 도움이 된다.
하지만 이 패턴을 남용하면, 직접 관리 할 수 없는 공간에 메모리가 계속 쌓이게 된다.
그렇기 때문에 소스코드의 신뢰성과 메모리의 문제를 함께 고민해서 적절히 사용해야 한다.
전역에 네임스페이스 역할을 담당할 객체를 생성하고, 전역 변수처럼 사용하고 싶은 변수를 프로퍼티로 추가한다.
이 방법은 식별자 충돌을 방지할 수는 있으나, 어쨌든 네임스페이스 객체 자체는 전역 변수에 할당된다.
var MYAPP = {}; // 전역 네임스페이스 객체
MYAPP.name = 'Lee';
console.log(MYAPP.name); // Lee
MYAPP.person = {
name: 'Lee',
address: 'Seoul'
};
console.log(MYAPP.person.name); // Lee
// a.js
var MYAPP = MYAPP || {};
MYAPP.name = 'Lee';
// b.js
var MYAPP = MYAPP || {};
MYAPP.name = 'Lee';
클래스를 모방해서 관련 있는 변수와 함수를 모아 즉시 실행 함수로 감싸 하나의 모듈을 만든다.
모듈 패턴은 자바스크립트의클로저
를 기반으로 동작하며,
전역 변수 억제는 물론캡슐화
까지 구현할 수 있다.
프로퍼티
와 메서드
를 하나로 묶는 것
프로퍼티
: 객체의 상태
메서드
: 프로퍼티를 참조하고 조작할 수 있는 동작
자바스크립트는 public
, private
, protected
등의 접근 제한자를 허용하지 않는다.
자바스크립트에서는 객체의 특정 프로퍼티나 메서드를 감추기 위해 모듈 패턴
을 사용한다.
// 객체를 반환하는 즉시 실행 함수
var Counter = (function () {
// private 변수
var num = 0;
// public 멤버 (외부로 공개할 데이터나 메서드를 프로퍼티로 추가한 객체)
return {
increase() {
return ++num;
},
decrease() {
return --num;
}
};
}());
// private 변수는 외부로 노출되지 않는다.
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
ES6 모듈은 파일 자체의 독자적인 모듈 스코프를 제공한다.
ES6 모듈 내에서 var 키워드로 선언한 변수는 전역 변수도, window 객체의 프로퍼티도 아니다.
script 태그에 type="module"
을 추가하면 파일은 모듈로서 동작하며, 모듈 파일 확장자는 mjs
를 권장한다.
<script type="module" src="app.mjs></script>
ES6 모듈은 구형 브라우저에서 동작할 수 없으며, 트랜스파일링이나 번들링이 필요하기 때문에 아직까지는 Webpack 등의 모듈 번들러를 사용하는 것이 일반적이다.