자바스크립트에서 아예 선언되지 않은 변수 체크하기

자바스크립트에서 아예 선언되지 않은 변수 체크하기

선언되지 않은 변수?

값이 정의되지 않은 변수선언되지 않은 변수를 구분해야 한다. 가장 흔히 보이는 값이 정의되지 않은 변수는 다음과 같다.

var a;
let b;

***const***는 초기값이 없는 상태로 초기화할 경우 SyntaxError를 발생시키므로 제외하였다. 굳이 하자면 const a = undefined가 될 수 있긴 하다.

위와 같은 코드는 남이 짜놓은 자바스크립트 코드를 보다 보면 흔하게 볼 수 있다. 혹은 그 사람은 그렇게 작성하지 않았더라도 minifier등으로 처리된 코드에서는 저런 부분이 많이 보이곤 했다.

값이 정의되지 않은 변수ab는 값이 할당되지 않아 보이지만 실제 스택에는 undefined가 값으로 할당된 상태이다. 즉 다음과 같다.

var a = undefined;
var b = undefined;

때문에 값이 정의되지 않은 변수 === 값이 undefined로 정의된 변수라고 보아도 무방하다. 이를 let으로 선언했을 경우 스코프의 영향을 받고, 스코프 밖에서 사용하려 하면 ReferenceError를 발생시킨다.

선언되지 않은 변수

선언되지 않은 변수는 위 내용에 이어서 생각하면 편하다. 아예 선언조차 되지 않은 변수, 즉 현재 스코프(글로벌 포함)에 아예 해당 변수 이름이 정의되지 않은, 콜 스택에 변수 참조가 없는 상태를 발한다. 즉 이는 변수조차 아니다.

그렇다면 어떤 경우에 이런 상황을 마주하게 될까? 사실, 일반적인 개발 상황에서 마주하기 쉬운 상태는 아니다. 그러나 나는 브라우저 확장 프로그램을 개발하려다가 이와 같은 상황을 마주치게 되었다.

content-script, background-script

어느 한 싱글톤 클래스의 메서드가 호출되었을 때, 호출한 당사자가 콘텐츠 스크립트인지 백그라운드 스크립트인지를 판별하려는 상황에서 이러한 문제가 발생했다. 처음엔 단순히 다음과 같이 작업하려 했다.

const isContentScript = () => !!document

그러나 위 코드는 콘텐츠 스크립트에서는 정상적으로 작동하나 백그라운드 스크립트 쪽에서는 Uncaught ReferenceError: document is not defined 예외를 던졌다.

왜냐? 백그라운드 스크립트엔 DOM이 존재하지 않고, 따라서 document도 없기 때문이다. 말 그대로, 없다. 선언조차 되지 않은 상태인 것이다.

따라서 해당 메서드가 백그라운드 스크립트에서 호출되었을 때, document라는 변수 이름을 자바스크립트 엔진이 스택에서 찾아 반환하려 시도하지만 이는 존재하지 않으므로 레퍼런스 에러를 던지는 것이다.

어느 변수가 선언된 상태인지 아닌지 확인하는 몇 가지 방법

그렇다면 도대체 어떤 식으로 확인해야 할까? 일단 콘텐츠 스크립트인지 백그라운드 스크립트인지 판별하는 가장 좋은 방법은1 여전히 document의 존재 여부이다. 따라서 첫 번째 방법으로는 다음과 같이 작성할 수 있다.

const isContentScript = () => {
	try {
		const _ = document;
		return true;
	} catch (error) {
		return false;
	}
}

document에 액세스를 시도하고, 성공했다면 정의되어 있는 상태이므로 true를, 아니라면 없는 상태이므로 false를 반환한다.

만약 위 메서드가 그렇게 자주 호출되지 않는다면 저런 식으로 사용해도 무관하나 일단 코드가 길고, try...catch문의 리소스 비용 문제도 있으므로 다음 방법을 사용하기로 했다.

const isContentScript = () => typeof document !== 'undefined';

한 줄로 깔끔해졌다. typeof 키워드는 선언되지 않은 변수에 대해 "undefined"를 반환한다. 따라서 첫 번째 방법보다 훨씬 깔끔하게 동일한 목적을 달성할 수 있다.
무엇보다 중요한 건, try…catch는 어느 언어든 상관없이 비용이 크다. 두 번째 방법 대비 첫 번째 방법의 실행 시간은 7배 이상 느리다. 따라서 단순히 어느 변수가 선언됐는지 안 됐는지 확인하고자 한다면 두 번째 방법인 typeof [varname] === 'undefined'를 사용해야 한다.


  1. 팝업 페이지는 논외. 만약 팝업 페이지에서까지 같이 사용하는 클래스라면 다른 방식을 취해야 한다. ↩︎

댓글

이 블로그의 인기 게시물

C# 남아도는 메모리에도 불구하고 OutOfMemoryException이 발생한다면?

USB를 뒤는 괜찮은데 앞에 꽂으면 인식이 힘들다?

MySQL 데이터 타입과 Java 데이터 타입 비교/매칭