[전역 유효 범위 오염]
전역 유효 범위를 오염시킨다는 말은 전역 변수와 전역 함수를 전역 객체에 선언하는 행위를 말한다. 전역 유효 범위가 오염되면 변수 이름과 함수 이름이 겹치는 상황이 발생한다. 다음과 같은 상황에서 많이 발생한다.
전역 유효 범위 안에서 이름이 같은 변수, 함수를 선언하면 자바스크립트 엔진은 호이스팅을 할 때 동일한 이름 중 하나만 생성한다. 따라서 해당 변수나 함수를 사용하는 다른 프로그램에서 오작동이 발생할 수 있다. 심지어 오류가 발생하지 않기 때문에 디버깅이 쉽지 않다.
예를 들어 한 페이지에서 a라는 모듈과 b라는 자바스크립트 모듈을 동시에 불러올 때 해당 모듈에 동일한 이름의 전역 변수가 선언되어 있다면?? 아래 예제를 살펴보자
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<script type="text/javascript" src="./test1.js"></script>
<script type="text/javascript" src="./test2.js"></script>
<body>
<h1>전역변수 중복</h1>
</body>
</html>
// test1.js
var global = 10;
// test2.js
var global = 20;
이렇게 var 키워드를 통해 global이라는 동일한 변수가 선언되었고 이는 최상위 레벨에서 선언되었기 때문에 전역 변수이다. 과연 해당 페이지에서 global을 참조할 때 어떤 값을 불러올까?? 바로20이다. 이것은 유효 범위 체인에 따라서 해당 변수 global의 식별자 결정이 된다. 자바스크립트의 식별자 결정 규칙은 좀 더 안쪽 코드에 선언된 변수를 사용한다는 것이다.
window.global;
// 20
전역 유효 범위의 오염을 최소하는 것이 좋은 개발 방식이다. 그렇다면 어떻게 할 수 있을까?? 바로 객체와 함수를 통해 네임스페이스를 만들고 참조 값을 할당하는 방식을 사용했다.
[네임스페이스 패턴]
네임스페이스(이름 공간, Namespace)란 이름이 존재하는 공간이다. 즉, 이름들을 한곳에 모아 충돌을 미리 방지하고 해당 이름으로 선언된 변수와 함수를 쉽게 가져다 쓸 수 있도록 만든 메커니즘이다. 네임스페이스 패턴은 애플리케이션 or 라이브러리를 위한 하나의 전역객체를 생성 후 그 안에 필요한 모든 기능을 프로퍼티로 정의하는 것이다.
[객체를 네임스페이스로 활용]
객체의 기초편에서 객체를 만드는 방식 중 하나로 객체 리터럴이 있었는데 이 객체 리터럴은 프로퍼티로 일반 값 뿐만아니라 함수(메서드)도 가능하기 때문이다.
// 객체 리터럴
var app = app || {};
// 프로퍼티 추가
app.name = "준영";
app.age = 30;
app.gender = "남";
app.nameCard = function(){
console.log('nameCard:', this); // 여기서 this는 app임
return `${this.age}세 ${this.name}`;
};
위와 같이 논리합 연산자를 사용하면 app이 기존에 정의되어 있을 대는 그것을 사용하고 그렇지 않으면 빈 객체를 app에 할당한다. 이렇게 하고 전역 유효 범위에서 사용하고자 하는 모든 변수와 함수를 이 객체의 프로퍼티로 추가하면 프로그램에서 app이라는 하나의 변수만 전역 객체 프로퍼티로 등록하여 해당 객체에서 필요한 모든 기능을 참조해 사용할 수 있다. 또한 이 객체 안에 또다른 부분 네임스페이스를 생성할 수 있다.
// 부분 네임스페이스 생성
app.subApp = {};
app.subApp.tempDate = new Date();
app.subApp.temp = function () {
console.log('temp:', this); // subApp을 가리킴
return `${app.name}의 입사일은 ${this.tempDate}입니다.`
}
부분 네임스페이스에선 root 프로퍼티가 또 다른 공간의 root가 되기 때문에 계층적으로 관리할 수 있다.
[함수를 네임스페이스로 활용]
자바스크립트의 함수 레벨 스코프에 따라 함수 안에서 선언된 변수의 유효 범위는 함수 내부이다. 오호라~ 그러면 함수 내부에 프로그램 작성하면 되겠구나?? 라고 생각할 수 있을 것이다. 바로 즉시 실행 함수를 활용한다면 해당 즉시 실행 함수 안에서 선언된 모든 것은 해당 함수의 지역 변수이므로 전역 변수이므로 라이브러리를 불러오고 해당 라이브러리 변수들이 전역으로 등록되어도 프로그램 내부에서 사용한 변수들과 전혀 겹치지 않는다. 따라서, 전역 공간을 전혀 건들지 않는다.
// 즉시 실행 함수
(function () {
// 이곳에 프로그램을 작성하자
})();
[모듈 패턴]
즉시 실행 함수를 사용하면 이처럼 전역 변수를 전혀 사용하지 않을 수 있지만 정작, 즉시 실행 함수에 정의된 변수나 기능이 필요한 경우엔 바깥에선 참조할 수 없기 때문에 사용할 수가 없다. 그렇다면 어떻게 해야할까?? 아래 코드에서 확인해보자.
var Module = Module || {};
(function (_Module) {
var name = "noName"; // 비공개
function getName() { // 비공개
return name;
}
_Module.showName = function () { // 공개
console.log(getName());
};
_Module.setName = function (x) { // 공개
name = x;
};
})(Module);
이런 것을 클로저라고 하는데 전형적인 모듈 패턴의 예다. 클로저는 다음 포스팅에서 살펴보도록 하자.
[ECMAScript 6 이후 let, const의 등장]
결론부터 말하자면 사실상 ECMAScript6 이후 let, const 선언 키워드가 등장하면서 이와 같은 문제를 상당 부분 해소됐다고 말할 수 있다고 조심스럽게 생각하고 있다. 왜냐하면 전역 환경(최상위 레벨)에서 let이나 const로 선언한 변수나 함수는 각각 전역 변수, 전역 함수가 맞지만 전역 객체에는 등록되지 않을 뿐더러 동일한 레벨 스코프에서 재선언을 허용하지 않기 때문이다. 자세한 사항은 이 글을 참고하자.
// let, const 전역 변수 선언
let global = 10;
window.hasOwnProperty("global"); // false
const global2 = 20;
window.hasOwnProperty("global2"); // false
let global = 10; // Uncaught SyntaxError: Identifier 'global' has already been declared
이럼 변수 선언할 때는 전역 객체에 등록되지 않고 동일한 이름의 변수를 재선언할 수 없다.
[JavaScript] 자바스크립트 화살표 함수(Arrow Function) (0) | 2020.08.26 |
---|---|
[JavaScript] 자바스크립트 클로저 (0) | 2020.08.24 |
[JavaScript] 자바스크립트 즉시 실행 함수 (0) | 2020.08.21 |
[JavaScript] 자바스크립트 var, let, const (2) | 2020.08.20 |
[JavaScript] 자바스크립트 스코프(scope) (0) | 2020.08.20 |