특수 값
타입별로 자바스크립트 개발자들이 조심해서 사용해야할 특수한 값들이 있다.
값 아닌 값
undefined
타입의 값은 undefined
밖에 없다. null
타입도 값은 null
뿐이다. 그래서 이 둘은 타입과 값이 항상 같다.
undefined
와 null
은 종종 '빈'값과 '값 아닌'값을 나타낸다. 이와 다른 의미로 사용하는 개발자도 있다. 예를들면,
- null은 빈 값이다.
- undefined는 실종된 값이다.
또는
- null은 예전에 값이 있었지만 지금은 없는 상태다.
- undefined는 값을 아직 가지지 않은 것이다.
undefined
와 null
의 의미를 어떻게 '정의'하여 쓰든지, null
은 식별자가 아닌 특별한 키워드 이므로 null
이라는 변수에 뭔가 할당할 수 는 없다. 오! 이런.. 그런데 불행히도 undefined
는 식별자로 쓸 수 있다.
Undefined
느슨한 모드에서는 전역 스코프에서 undefined
란 식별자에 값을 할당할 수 있다.(절대 추천하지 않는다.)
function foo() {
undefined = 2; // 진짜 진짜 비추천한다.
}
foo();
function foo() {
"use strict";
undefined = 2; // 타입에러 발생!!
}
foo();
그런데 모드에 상관없이 undefined
란 이름을 가진 지역 변수는 생성할 수 있다. 가능하기는 하지만 끔직한 짓이다.
function foo() {
"use strict";
var undefined = 2;
console.log(undefined); // 2
}
foo();
좋은 친구라면 undefined
를 여러분이 재정의 하도록 내버려 두지 않을 것이다.
void 연산자
undefined
는 내장 식별자로 값은 undefined
지만, 이 값은 void
연산자로도 얻을 수 있다. 표현식 void __
는 어떤 값이든 '무효로 만들어'. 항상 결과 값을 undefined
로 만든다. 기존 값은 건드리지 않고 연산 후 값은 복구할 수 없다.
var a = 42;
console.log(void a, a); // undefined 42
관례에 따라 void
만으로 undefined
값을 나타내려면 void 0
이라고 쓴다. void 0
, void 1
, undefined
모두 같다.
void
연산자는 어떤 표현식의 결괏값이 없다는 걸 확실히 밝혀야 할 때 긴요하다. 예를들어
function doSomething() {
// 참고 : APP.ready는 이 애플리케이션에서 제공한 값이다.
if (!APP.ready) {
// 나중에 다시 해보자
return void setTimeout(doSomething, 100);
}
var result;
// 별도 처리 수행
return result;
}
// 제대로 처리했니?
if (doSomething()) {
// 다음작업 바로실행
}
setTimeout()
함수는 숫자 값(타이머를 취소할 때 사용할 타이머의 고유 식별자)을 반환하지만, 예제 에서는 이 숫자 값을 무효로 만들어 doSomething()
함수의 결괏값이 if
문에서 긍정 오류가 일어나지 않게 했다.
이때 다음 코드처럼 두 줄로 분리해 쓰는 걸 선호하는 개발자들이 많고 void
연산자는 잘 쓰지 않는다.
if (!APP.ready) {
// 나중에 다시 해보자!
setTimeout(doSomething, 100);
return;
}
정리하면 void
연산자는 값이 존재하는 곳에서 그 값이 undefined
가 되어야 좋을 경우에만 사용하자. 아마도 그렇게 해야 할 경우도 거의 없고 극히 제한적으로 쓰이겠지만 제법 쓸모는 있다.
특수 숫자.
숫자 타입에는 몇 가지 특수한 값이 있다. 하나씩 알아보자.
The not number, number
수학 연산 시 두 피연산자가 전부 숫자가 아닐 경우 유효한 숫자가 나올 수 없으므로 그 결과는 NaN
이다. NaN
은 글자 그대로 '숫자 아님(Not A Number)'이다. 그런데 이 명칭과 설명이 아주 형편없고 오해의 소지가 다분하다. NaN
은 숫자 아님 보다는 유효하지 않은 숫자, 실패한 숫자 또는 몹쓸 숫자라고 하는게 차라리 더 정확하다.
var a = 2 / "foo"; // NaN
typeof a === "number"; // true
즉, '숫자 아님의 typeof
는 숫자다!'란 뜻이다. 숫자가 아니라며 숫자라는 해괴망측한 이름과 의미를 가지고있다.
NaN
은 경계 값의 일종으로 숫자 집합 내에서 특별한 종류의 에러상황("나는 당신이 내준 수학 연산을 해봤지만 실패했어 그러니 여기 실패한 숫자를 도로 가져가시지")을 나타낸다.
어떤 변숫값이 특수한 실패 숫자, 즉 NaN
인지 여부를 확인할 때 null
, undefined
처럼 NaN
도 직접 비교하고 싶은 충동이 생기겠지만.. 틀렸다.
var a = 2 / "foo";
a == NaN; // false
a === NaN; // false
NaN
은 귀하신 몸이라 다른 어떤 NaN
과도 동등하지 않다.(즉, 자기 자신과도 같지 않다.) 사실상 반사성이 없는 유일무이한 값이다. 따라서 NaN !== NaN
이다.
비교 불가능이라면 그럼 NaN
여부는 어떻게 확인할까?
var a = 2 / "foo";
isNaN(a); // true
내장 전역 유틸리티 isNaN()
함수가 NaN
여부를 확인한다. 문제 해결!!! 인줄 알았겠지만 아니다. isNaN()
는 치명적인 결함이 있다. 이 함수는 NaN
의 의미를 너무 글자 그대로만 해석해서 실제로 '인자 값이 숫자인지 여부를 평가'하는 기능이 전부다. 하지만 이래서는 결과가 적확할 수 없다.
var a = 2 / "foo";
var b = "foo";
a; // NaN
b; // "foo"
window.isNaN(a); // true
window.isNaN(b); // true ...?
"foo"는 당연히 숫자가 아니지만, 그렇다고 NaN
는 아니다! 이 버그는 자바스크립트 탄생 이후 오늘까지 계속됬다. 19년이 넘도록..
드디어 ES6부터는 해결사 Number.isNaN()
이 등장했다. ES6 이전 브라우저에서는 다음 폴리필을 쓰면 안전하게 NaN
여부를 체크하 수 있다.
if (!Number.isNaN) {
Number.isNaN ===
function (n) {
return typeof n === "number" && window.isNaN(n);
};
}
var a = 2 / "foo";
var b = "foo";
Number.isNaN(a); // true
Number.isNaN(b); // false !!
NaN
이 자기 자신과도 동등하지 않는 독특함을 응용하여 폴리필을 더 간단히 구현할 수도 있다. NaN
은 세상의 모든 언어를 통틀어 "자기 자신과도 동등하지 않은" 유일한 값이다.
if (!Number.isNaN) {
Number.isNaN = function (n) {
return n !== n;
};
}
이상하게 보이지만 잘 작동한다!! 실제로 많은 자바스크립트 코드에 NaN
은 고의 또는 실수로 박혀있다. 의미를 오해하지 않고 바르게 쓰려면 Number.isNaN
같은 내장 유틸리티를 사용하다. 만약 지금 여러분이 isNaN()
를 사용하여 코딩 중이라면, 유감스럽게도 아직 터지지 않은 지뢰를 묻어 놓은 셈이다.
무한대
C와 같은 전통적인 컴파일 언어를 쓰는 개발자들은 '0으로 나누기'비슷한 컴파일/런타임 에러를 숱하게 보았을 것이다.
var a = 1 / 0;
그러나 자바스크립트에서는 0으로 나누기 연산이 잘 정의되어 있어서 에러 없이 Infinity(Number.POSITIVE_INFINITY)
라는 결과값이 나온다.
var a = 1 / 0; // Infinity
var b = -1 / 0; // -Infinity
분자가 음수면 0으로 나누기 결과 값은 -Infinity
다. 자바스크립트는 유한 숫자 표현식을 사용하므로 수학 교과서와는 다르게 덧셈, 뺄셈 같은 연산 결과가 +무한대/-무한대가 될 수 있다.
"무한을 무한으로 나누면?" 이거 단순히 생각하면 '1'이나 '무한대'가 될 것 같지만 모두 틀렸다. 수학책, 자바스크립트 모두 무한대/무한대는 '정의되지 않는 연산'이며, 결괏값은 NaN
이다!
유한한 양수를 무한대로 나누면? 당연히 0, 이건 쉽다 그렇다면 음수를 무한대로 나누면?
영(0)
자바스크립트엔 보통 영(+0)과 음의 영(-0)이 있다는 사실 자체가 혼란스러울 수도 있다. -0의 존재 이유를 설명하기 전에 자바스크립트가 영을 다루는 방식을 먼저 짚어보자.
음의 영은 표기만 -0으로 하는 것이 아니다. 특정 수식의 연산 결과 또한 -0으로 떨어진다.
var a = 0 / -3; // -0
var b = 0 * -3; // -0
덧셈과 뺄셈에는 -0이 나올 일이 없다. 개발자 콘솔 창에서 확인해보면 -0으로 나오겠지만, 비교적 최근 가지 자주 나오는 연산은 아니여서 아직도 0으로 표시되는 브라우저도 있다. 그런데 무슨 수학 경시대회도 아니고 -0은 왜만든 것일까?
값의 크기로 어떤 정보 어떤정보와 그 값의 부호로 또 다른 정보를 동시에 나타내야 하는 애플리케이션이 있기 때문이다. +0, -0 개념이 없다면 어떤 변숫값이 0에 도달하여 부호가 바뀌는 순간, 그 직전까지 이 변수의 이동 방향은 무엇인지 알 수가 없으므로 부호가 다른 두 0은 유용하다. 즉, 잠재적인 정보 소실을 방지하기 위해 0의 부호를 보존한 셈이다.
특이한 동등 비교
앞서 설명했듯이 NaN
과 -0의 동등 비교는 독특하다. NaN
은 자기자신과도 동등하지 않아 ES6의 Number.isNaN()
또는 폴리필을 사용해야 하며, 마찬가지로 -0도 거짓말 쟁이라서 보통의 0과 동등한 척 하므로(심지어 엄격 동등 연산자 ===
에서도), isNegZero()
같은 함수를 꼼수로 써야한다.
ES6부터는 잡다한 예외를 걱정하지 않아도 두 값이 절대적으로 동등한지를 확인하는 새로운 유틸리티를 지원한다. 바로 Object.is()
다.
var a = 2 / "foo";
var b = -3 * 0;
Object.is(a, NaN); // true
Object.is(b, -0); // true
Object.is(b, 0); // false
ES6 이전 환경에서도 Object.is()
폴리필을 만들어 간단히 쓸 수 있다.
if (!Object.is) {
Object.is = function (v1, v2) {
// '-0' 테스트
if (v1 === 0 && v2 === 0) {
return 1 / v1 === 1 / v2;
}
// 'NaN' 테스트
if (v1 !== v1) {
return v2 !== v2;
}
// 기타
return v1 === v2;
};
}
==
나 ===
가 안전하다면 굳이 Object.is()
는 사용하지 않는 편이 좋다. 아무래도 기본 연산자가 돔 더 효율이 좋고 알반적이기 때문이다. Object.is()
는 주로 특이한 동등 비교에 쓴다.
'JAVASCRIPT' 카테고리의 다른 글
[JS] this 란? (0) | 2022.01.26 |
---|---|
[JS][값][마무리] 값 vs 레퍼런스 (0) | 2022.01.26 |
[JS][값] 숫자 (0) | 2022.01.25 |
[JS][값] 문자열 (0) | 2022.01.24 |
[JS][값] Array (0) | 2022.01.24 |
댓글