Notice
Recent Posts
Recent Comments
Link
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
Archives
Today
Total
관리 메뉴

nhyunzi

[코딩앙마] 자바스크립트 기초 강좌 #2 - 자료형 본문

카테고리 없음

[코딩앙마] 자바스크립트 기초 강좌 #2 - 자료형

nhyunzi 2025. 1. 11. 22:25

 

숫자에 관련된 작업을 할때 항상 NaN이 아닌지 염두에 둔다. 

 

왜 염두에 두어야 할까?

NaN이 연산 결과나 비교에서 예상치 못한 오류를 일으킬 수 있기 때문이다.

 

1. NaN은 여러 연산에서 자동으로 발생할 수 있다. 

let result = 0 / 0; // 0을 0으로 나누면 NaN이 발생
console.log(result); // NaN

let invalidNumber = Number("hello"); // 문자열을 숫자로 변환할 수 없어서 NaN
console.log(invalidNumber); // NaN

 

2. NaN이 있으면 연산 결과가 이상해질 수 있다

NaN이 포함된 연산에서는 예기치 않은 결과가 나올 수 있다.

NaN과 다른 숫자와의 연산은 항상 NaN을 반환한다.

따라서 NaN이 연산에 포함되면, 연산 결과가 의도한 대로 나오지 않게 된다. 

let value = 5 + NaN; // NaN이 포함되면 결과가 NaN이 됨
console.log(value); // NaN

 

3. NaN을 비교할 때 주의가 필요

NaN은 자기 자신과 비교해도 false가 된다. 즉, NaN을 포함하는 변수는 단순히 비교로 NaN을 확인할 수 없다.

let x = NaN;
console.log(x === NaN); // false

 

NaN을 비교하려면 isNaN() 또는 Number.isNaN() 함수를 사용해야 한다.

let x = NaN;
console.log(isNaN(x)); // true
console.log(Number.isNaN(x)); // true

 

NaN을 방지하는 방법?

입력단계에서 NaN 발생을 차단하도록 하고  만약 예기치 않게 발생할 수 있다면

isNaN() 함수나 Number.isNaN() 메서드를 활용해 NaN을 검사하여 유효한 숫자만 연산에 포함되도록 할 수 있다. 

function addNumbers(a, b) {
  if (isNaN(a) || isNaN(b)) { // `NaN` 확인
    console.log("잘못된 입력! 숫자만 입력해야 합니다.");
    return;
  }
  return a + b;
}

console.log(addNumbers(5, 10)); // 15
console.log(addNumbers(5, "hello")); // 잘못된 입력! 숫자만 입력해야 합니다.

사용하는 개발자가 변수를 직접 작성했다면 typeof 연산자를 거의 사용할 일이 없지만 
다른 개발자가 작성한 변수의 타입을 알아야 하거나,

api통신 등을 통해 받아온 데이터를 type에 따라 다른 방식으로 처리해야 할 때 많이 사용한다. 

typeof null; // "object" 객체형

 

사실은 null은 객체가 아니다. 
자바스크립트의 초기버전의 오류로, 하위호환성 유지하기 위해서 수정되지 않고 있다. 

 

 

+ 궁금했던 부분 추가

 

1. NaN을 비교할때 isNaN(), Number.isNaN() 을 쓸수 있다고 했는데 Number.isNaN은 왜 이름이 Number.isNaN()이지?

 

초기 자바스크립트에서는 isNaN이 전역 함수로 존재했다.

 

(isNaN의 내부 동작 원리)
isNaN(x) ⟶ Number(x) 변환 ⟶ 변환 결과가 NaN인지 확인

console.log(isNaN("hello")); 	// true 
console.log(isNaN(undefined)); 	// true 
console.log(isNaN("123")); 	// false 
console.log(isNaN(true)); 	// false

 

 

위의 예시처럼 숫자로 변환할 수 없는 값이 NaN이 되어,
isNaN()이 true를 반환하는 잘못된 결과가 발생했다.

 

이 문제를 해결하기 위해 ES6에서 Number.isNaN()이 추가되었다.

Number.isNaN()은 숫자가 아닌 값(string, undefined, null 등)을 자동 변환하지 않고,

오직 숫자인 경우만 NaN인지 판별한다.

console.log(Number.isNaN("hello")); 	// false 
console.log(Number.isNaN(undefined)); 	// false 
console.log(Number.isNaN(NaN)); 	// true

 

Number.isNaN()은 기존 isNaN()이 숫자가 아닌 값까지 잘못 판별하는 문제를 해결하기 위해,
Number 객체의 메서드로 따로 분리되었다.

즉, Number.isNaN()은 오직 NaN인 경우만 true를 반환한다.

기존 isNaN()은 레거시 코드 유지를 위해 남아 있지만,
ES6 이후로는 Number.isNaN()을 표준으로 사용하는 것이 좋고 안전하다.

 

2. typeof null은 왜 Object로 나올까?

 

초창기 js의 버그다. 초기 자바스크립트 엔진은 값을 저장할 때, 메모리의 첫 몇비트를 이용해서 타입을 구분했다.
이때 객체는 메모리 내부적으로 000으로 시작하는 특별한 비트를 가졌고,
null은 메모리 주소가 00000000(비어있는 값)으로 설정되었다.
초창기 자바스크립트 엔진이 메모리 내부의 null값을 객체로 잘못해석하면서 Object로 반환하는 버그가 발생한 것이다.

자바스크립트가 널리 사용되면서 이 버그를 고치면 기존 코드들이 깨질 위험이 있었고 
ECMA script 표준에서 이 버그를 유지하기로 결정했기 때문에 지금도 Object로 나온다. 

 

아래와 같은 방법으로 null을 정확히 확인할 수있다. 

// 1. 직접비교 
console.log(null === null); // true 

// 2. null인지 확인하는 함수 만들기
function isNull(value) {
  return value === null;
}

console.log(isNull(null)); // true 
console.log(isNull(undefined)); // false 
console.log(isNull({})); // false

// 3. Object.prototype.toString.call() 활용
console.log(Object.prototype.toString.call(null)); // "[object Null]"

 

3. Object.prototype.toString.call()이 뭘까?

 

typeof는 객체(null, array, date 등)를 정확히 구분하지 못하는 단점이 있다.

console.log(typeof null); 	// "object" 
console.log(typeof []);   	// "object" 
console.log(typeof {});   	// "object" 
console.log(typeof new Date()); // "object"

 

Object.prototype.toString.call()을 이용하면 모든 타입이 정확히 출력된다. 

 

Object.prototype.toString은 객체의 내부 [[Class]] 속성을 문자열로 반환하는 메서드다.
그런데 기본적으로 this가 Object 타입이어야 정상 동작한다.


call()을 사용하면 this를 Object로 변환하여 Object.prototype.toString을 실행할 수 있고,

이를 통해 정확한 타입을 얻을 수 있다.

console.log(Object.prototype.toString.call(null));       // "[object Null]" 
console.log(Object.prototype.toString.call([]));         // "[object Array]" 
console.log(Object.prototype.toString.call({}));         // "[object Object]" 
console.log(Object.prototype.toString.call(new Date())); // "[object Date]" 
console.log(Object.prototype.toString.call(/regex/));    // "[object RegExp]" 
console.log(Object.prototype.toString.call(123));        // "[object Number]"
console.log(Object.prototype.toString.call("hello"));    // "[object String]" 
console.log(Object.prototype.toString.call(true));       // "[object Boolean]" 
console.log(Object.prototype.toString.call(undefined));  // "[object Undefined]" 

// Object.prototype.toString.call()을 활용한 타입 체크 함수
function getType(value) {
  return Object.prototype.toString.call(value).slice(8, -1);
}

console.log(getType(null));       // "Null"
console.log(getType([]));         // "Array"
console.log(getType({}));         // "Object"
console.log(getType(new Date())); // "Date"
console.log(getType(/regex/));    // "RegExp"
console.log(getType(123));        // "Number"
console.log(getType("hello"));    // "String"
console.log(getType(true));       // "Boolean"
console.log(getType(undefined));  // "Undefined"