전역 NaN 속성은 Not-A-Number(숫자가 아님)를 나타낸다.
function sanitise(x) {
if (isNaN(x)) {
return NaN;
}
return x;
}
console.log(sanitise('1'));
// Expected output: "1"
console.log(sanitise('NotANumber'));
// Expected output: NaN
NaN은 전역 객체의 속성이다. 즉 전역 범위의 변수다.
최신 브라우저에서 NaN은 설정 불가, 쓰기 불가 속성이다. 그렇지 않다고 하더라도 덮어쓰는 건 피하는 게 좋다.
NaN을 반환하는 연산에는 다섯 가지 종류가 있다.
- 숫자로 변환 실패 (예시: parseInt("blabla"), Number(undefined)와 같은 명시적인 것 또는 Math.abs(undefined)와 같은 암시적인 것)
- 결과가 허수인 수학 계산식 (예시: Math.sqrt(-1))
- 정의할 수 없는 계산식 (예시: 0 * Infinity, 1 ** Infinity, Infinity / Infinity, Infinity - Infinity)
- 피연산자가 NaN이거나 NaN으로 강제 변환되는 메서드 또는 표현식 (예시: 7 ** NaN, 7 * "blabla") - 이것은 NaN이 전염성 있다는 것을 의미한다.
- 유효하지 않은 값이 숫자로 표시되는 기타 경우 (예시: 잘못된 날짜 new Date("blabla").getTime(), "".charCodeAt(1))
NaN과 NaN의 동작은 JavaScript에서 발명한 것이 아니다. 부동 소수점 산술의 의미(NaN !== NaN 포함)는 IEEE 754에 의해 지정되었다. NaN의 동작은 다음과 같다.
- NaN이 수학 연산에 포함된 경우 (그러나 비트 연산는 아님) 결과도 일반적으로 NaN 이다. (아래의 counter-example 참조)
- NaN이 관계 비교(>, <, >=, <=)의 피연산자 중 하나인 경우 결과는 항상 false이다.
- NaN은 ( ==, !=, === (en-US) 및 !== (en-US) 를 통해) 다른 NaN 값을 포함하여 다른 값과 같지 않은 것으로 비교된다.
NaN은 JavaScript의 falsy 값 중 하나이다.
NaN 판별
값이 NaN인지 확인하려면, Number.isNaN() 또는 isNaN()를 사용하여 값이 NaN인지 여부를 확인 할 수 있다. 또는 NaN은 자신과 같지 않다고 비교되는 유일한 값이므로 x !== x와 같은 자체 비교를 수행할 수 있다.
NaN === NaN; // false
Number.NaN === NaN; // false
isNaN(NaN); // true
isNaN(Number.NaN); // true
function valueIsNaN(v) {
return v !== v;
}
valueIsNaN(1); // false
valueIsNaN(NaN); // true
valueIsNaN(Number.NaN); // true
그러나 isNaN()과 Number.isNaN()의 차이를 유의해야 한다. isNaN은 현재 값이 NaN이거나, 숫자로 변환했을 때 NaN이 되면 참을 반환하지만, Number.isNaN은 현재 값이 NaN이어야만 참을 반환한다.
isNaN("hello world"); // true
Number.isNaN("hello world"); // false
같은 이유로 BigInt 값을 사용하면 Number.isNaN()이 아닌 isNaN()에서 오류가 발생한다.
isNaN(1n); // TypeError: Conversion from 'BigInt' to 'number' is not allowed.
Number.isNaN(1n); // false
또한 일부 배열 메서드는 NaN을 찾을 수 없는 반면에 다른 배열 메서드들은 찾을 수 있다. 즉, (indexOf(), lastIndexOf())는 NaN을 찾을 수 없지만, includes()는 값을 찾을 수 있다.
const arr = [2, 4, NaN, 12];
arr.indexOf(NaN); // -1
arr.includes(NaN); // true
// 적절하게 정의된 조건자를 허용하는 메서드는 항상 NaN을 찾을 수 있다.
arr.findIndex((n) => Number.isNaN(n)); // 2
NaN과 그 비교에 대한 자세한 내용은 평등 비교 및 동일성을 참조한다.
눈에 띄게 구별되는 NaN 값
NaN이 자신과 동등하지 않은 데는 동기가 있다. IEEE 754 인코딩에서 지수 0x7ff와 0이 아닌 가수부가 있는 부동 소수점 숫자는 NaN이기 때문에 서로 다른 이진 표현을 가진 두 개의 부동 소수점 숫자를 생성할 수 있지만 둘 다 NaN이다. JavaScript에서 typed arrays를 사용하여 비트 수준 조작을 수행할 수 있다.
const f2b = (x) => new Uint8Array(new Float64Array([x]).buffer);
const b2f = (x) => new Float64Array(x.buffer)[0];
// NaN의 byte 표현을 가져온다.
const n = f2b(NaN);
// 부호 비트이고 NaN에 중요하지 않은 첫 번째 비트를 변경한다.
n[0] = 1;
const nan2 = b2f(n);
console.log(nan2); // NaN
console.log(Object.is(nan2, NaN)); // true
console.log(f2b(NaN)); // Uint8Array(8) [0, 0, 0, 0, 0, 0, 248, 127]
console.log(f2b(nan2)); // Uint8Array(8) [1, 0, 0, 0, 0, 0, 248, 127]
조용히 NaN 탈출
NaN은 수학적 연산을 통해 전파되므로 일반적으로 오류 조건을 감지하기 위해 계산이 끝날 때 한 번 NaN을 테스트하는 것으로 충분하다. NaN이 자동으로 이스케이프되는 유일한 경우는 지수가 0인 거듭제곱을 사용할 때이다. 그러면 기본값을 검사하지 않고 즉시 1이 반환된다.
NaN ** 0 === 1; // true