14장 전역 변수의 문제점
14.1 변수의 생명 주기
14.1.1 지역 변수의 생명 주기
변수는 생물과 유사하게 생성되고 소멸되는 생명 주기가 있다. 변수에 생명 주기가 없다면 한번 선언된 변수는 프로그램을 종료하지 않는 한 영원히 메모리 공간을 점유하게 된다.
지역 변수의 생명주기는 함수의 생명 주기와 일치한다.
14.1.2 전역 변수의 생명 주기
var 키워드로 선언한 전역 변수는 전역 객체의 프로퍼티가 된다. 이는 전역 변수의 생명 주기가 전역 객체의 생명 주기와 일치한다는 것을 말한다.
14.2 전역 변수의 문제점
- 암묵적 결합: 모든 코드가 전역 변수를 참조하고 변경할 수 있다.
- 긴 생명 주기: 메모리 리소스를 오랜 기간 소비한다. 또한 전역 변수의 상태를 변경할 수 있는 시간도 길고 기회도 많다.
- 스코프 체인 상에서 종점에 존재: 전역 변수의 검색 속도가 가장 느리다.
- 네임스페이스 오염: 자바스크립트의 가장 큰 문제점 중 하나는 파일이 분리되어 있다 해도 하나의 전역 스코프를 공유한다는 것이다. 따라서 다른 파일 내에서 동일한 이름으로 명명된 전역 변수나 전역 함수가 같은 스코프 내에 존재할 경우 예상치 못한 결과를 가져올 수 있다. 운영체제에서 네임스페이스는 디렉토리(directory)이다(참조: MDN 문서). 다른 디렉토리로 분리할 경우 전역 스코프는 공유되지 않는다.
14.3 전역 변수의 사용을 억제하는 방법
- 즉시 실행 함수
- 네임스페이스 객체 : 전역에 네임스페이스(name space) 역할을 담당할 객체를 생성하고 전역 변수처럼 사용하고 싶은 변수를 프로퍼티로 추가하는 방법
- 모듈 패턴: 즉시 실행 함수로 관련있는 변수와 함수를 모아 감싼 것. 모듈 패턴은 자바스크립트의 강력한 기능인 클로저를 기반으로 동작한다. 모듈 패턴의 특징은 전역 변수의 억제는 물론 캡슐화까지 구현할 수 있다는 것이다.
- ES6 모듈: es6 모듈을 사용하면 더는 전역 변수를 사용할 수 없다. Es6 모듈은 파일 자체의 독자적인 모듈 스코프를 제공한다. 따라서 모듈 내에서 var 키워드로 선언한 변수는 더는 전역 변수가 아니며 window 객체의 프로퍼티도 아니다. 모던 브라우저에서는 es6모듈을 사용할 수 있다.
15장 Let, const 키워드와 블록 레벨 스코프
15.1 var 키워드로 선언한 변수의 문제점
- 변수 중복 선언 허용
- 함수 레벨 스코프 : var 키워드로 선언한 변수는 오로지 함수의 코드 블록만을 지역 스코프로 인정한다. 따라서 함수 외부에서 var 키워드로 선언한 변수는 코드 블록 내에서 선언해도 모두 전역 변수가 된다.
- 변수 호이스팅: 변수 선언문이 스코프의 선두로 끌어올려진 것처럼 동작한다.
15.2 let 키워드
- 변수 중복 선언 금지
- 블록 레벨 스코프 : let 키워드로 선언한 변수는 모든 코드 블록(함수, if문, for문, while문, try/catch문 등)을 지역 스코프로 인정하는 블록 레벨 스코프(block-level scope)를 따른다.
- 변수 호이스팅: let 키워드로 선언한 변수는 “선언 단계”와 “초기화 단계”가 분리되어 진행된다. 즉 런타임 이전에 자바스크립트 엔진에 의해 암묵적으로 선언 단계가 먼저 실행되지만 초기화 단계는 변수 선언문에 도달했을 때 실행된다. 만약 초기화 단계가 실행되기 이전에 변수에 접근하려고 하면 참조 에러(reference error)가 발생한다. Let 키워드로 선언한 변수는 스코프의 시작 지점부터 초기화 단계 시작 지점(변수 선언문)까지 변수를 참조할 수 없다. 스코프의 시작 지점부터 초기화 시작 지점까지 변수를 참조할 수 없는 구간을 “일시적 사각지대(Temporal Dead Zone, TDZ)”라고 부른다.
- 전역 객체와 let: var 키워드로 선언한 전역 변수, 전역 함수, 암묵적 전역은 전역 객체 window의 프로퍼티가 된다. 그러나 let 키워드로 선언한 전역 변수는 전역 객체의 프로퍼티가 아니다. Let 전역 변수는 보이지 않는 개념적인 블록(전역 렉시컬 환경의 선언적 환경 레코드) 내에 존재하게 된다.
15.3 const 키워드
- 선언과 초기화 : const 키워드로 선언한 변수는 반드시 선언과 동시에 초기화해야 한다.
- 재할당 금지
- 상수: 변수의 상대 개념인 상수는 재할당이 금지된 변수를 말한다. 상수는 상태 유지와 가독성, 유지보수의 편의를 위해 적극적으로 사용해야 한다. 일반적으로 상수의 이름은 스네이크 케이스로 표현한다.
- Const 키워드와 객체 : const 키워드로 선언한 변수에 원시 값을 할당한 경우 값을 변경할 수 없지만, 객체를 할당한 경우 값을 변경할 수 있다.
16장. 프로퍼티 어트리뷰트
16.1 내부 슬롯과 내부 메서드
- 내부 슬롯(internal slot): 의사 프로퍼티(pseudo property)
- 내부 메서드(internal method): 의사 메서드(pseudo method)
- 내부 슬롯과 내부 메서드는 자바스크립트 엔진의 내부 로직이므로 원칙적으로는 직접적으로 접근하거나 호출할 수 있는 방법을 제공하지 않지만 일부 내부 슬롯과 내부 메서드에 한하여 간접적으로 접근할 수 있는 수단을 제공한다.
- 예를 들어, 모든 객체는 [[Prototype]] 이라는 내부 슬롯을 갖는데, 내부 메서드 proto를 통해 내부 슬롯에 간접적으로 접근할 수 있다.
16.2 프로퍼티 어트리뷰트와 프로퍼티 디스크립터 객체
자바스크립트 엔진은 프로퍼티를 생성할 때 프로퍼티의 상태를 나타내는 프로퍼티 어트리뷰트를 기본값으로 자동 정의한다. 프로퍼티의 상태란
- value: 프로퍼티의 값.
- writable: 값의 갱신 가능 여부.
[[Writable]]의 값이 false인 경우 해당 프로퍼티의 [[Value]]의 값은 변경할 수 없는 읽기 전용 프로퍼티이다.
- enumerable: 열거 가능 여부.
[[Enumerable]]의 값이 false인 경우 해당 프로퍼티는 for ... in 문이나 Object.keys 메서드 등으로 열거할 수 없다.
- configurable: 재정의 가능 여부.
[[Configurable]]의 값이 false인 경우 해당 프로퍼티의 삭제,프로퍼티 어트리뷰트 값의 변경이 금지된다.
를 말한다. 프로퍼티 어트리뷰트(property attribute)는 자바스크립트 엔진이 관리하는 내부 슬롯이다. 따라서 프로퍼티 어트리뷰트에 직접 접근할 수 없지만 Object.getOwnPropertyDescriptor 메서드를 사용해 간접적으로 확인할 수는 있다. Object.getOwnPropertyDescriptor 메서드는 프로퍼티 어트리뷰트 정보를 제공하는 프로퍼티 디스크립터(Property Descriptor) 객체를 반환한다.
const person = {
name: 'Lee'
};
console.log(Object.getOwnPropertyDescriptor(person, 'name'));
ES8에서 도입된 Object.getOwnPropertyDescriptors 메서드는 모든 프로퍼티의 프로퍼티 어트리뷰트 정보를 제공하는 프로퍼티 디스크립터 객체들을 반환한다.
16.3 데이터 프로퍼티와 접근자 프로퍼티
프로퍼티는 데이터 프로퍼티와 접근자 프로퍼티로 구분할 수 있다.
- 데이터 프로퍼티(data property)
키와 값으로 구성된 일반적인 프로퍼티
- 접근자 프로퍼티(accessor property)
자체적으로는 값을 갖지 않고 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 호출되는 접근자 함수(accessor function)로 구성된 프로퍼티