[Effective JavaScript] 암묵적인형변환에 주의하라

김범식·2023년 6월 14일
0

Effective JavaScript

목록 보기
21/33
post-thumbnail

javascript는 데이터 형 오류에 놀라울 정도로 관대하다

3 + true // 4

많은 언어들은다음과 같은 표현식을 오류로 처리한다.

이런 자바스크립트도 오류로 보여주는 몇가지 자료형이 있다.

"hello"(1); // 오류 : 함수가 아님
null.x;  // 오류 : null에서 프로퍼티 'x'를 읽어올 수 없음 

하지만 다른경우 자바스크립트는 오류를 발생시키는 대신, 자동형변환 프로토콜에 따라 예상된 데이터형으로 값을 형변환 한다. -, *, /, %, 는 계산전에 인자들을 숫자형으로 변환한다. + 연산자는 조금 특이한데 인자들의 데이터 형에따라 오버로딩된다.

2 + 3 // 8
"hello" + " world" // hello world
"2" + 3 // "23"
3 + "2" // "23"

비트단위 연산은 숫자로 변환할 뿐만 아니라, 32비트 정수로 표현될 수 있는 숫자의 부분집합으로도 변환한다. 이런 특징은 비트단위 산술 연산자 (~, & , ^, |) 와 시프트 연산자 ( <<, >>, >>> )에 적용된다.

이러한 형변환은 다음과 같은 상황에서 매우 편리해보인다

"17" * 3 ; // 51
"8" | "1" ; // 9

하지만 형변환은 오류도 숨길 수 있다. null로 판단되는 변수가 산술연산에서 오류를 발생하지 않은 채 조용히 0으로 변환되고 정의되지 않은 변수가 특별한 부동 소수점 값인 NaN(not a number)으로 변환된다. 이런 형변환은 즉시 예외르 ㄹ발생시키지 않고, 계산을 계속하게 해서 혼란스럽고 예측하기 어려운 값들을 자주 만들어 낸다.

NaN

NaN은 JavaScript의 특수한 값으로, "Not a Number"을 의미한다. NaN은 숫자가 아닌 연산의 결과를 나타내는 데 사용한다.

NaN 값을 테스트하기는 특히나 어렵다. 다음과 같은 이유 때문이다.

var x = NaN;
x === NaN; // false

표준 isNaN 라이브러리 함수는 스스로 암묵적인 형변환, 즉 값을 테스트하기 전에 인자를 숫자로 바꾸기 때문에 신뢰할 만하지 않다.

isNaN(NaN); //true

NaN으로 강제 형변환할 수 있는 다른 값들은, 실제로 NaN이 아니라면 isNaN으로 구별할 수 없다.

isNaN("foo") //true;
isNaN(undefined) //true;
isNaN({}) //true;
isNaN({valueOf : "foo"}) //true;

다행이도 다소 직관적이지는 않지만 NaN을 테스트하기 위한 간결하고 신뢰할만한 코딩 관례가 있다.

var a = NaN;
a !== a // true

var b = "foo"
b !== b // false

var c = undefined
c !== c // false

var b = {}
b !== b // false

var e = {valueOf : "foo"}
e !== e // false

이 패턴을 다음과 같은 명백한 이름의 유틸리티 함수로 추상화 할 수 있다.

function isReallyNan(x){
	return x !== x
}

물론 너무나도 간단한 로직이기 때문에 굳이 이런 함수로 만들어 사용하지는 않는다.

객체 또한 원시 데이터형으로 강제 형변환될 수 있다.

"the Math object" + Math; // "the Math Objet [object Math]"
"the Math object" + JSON; // "the Math Objet [object JSON]"

객체는 암묵적으로 내부의 toString() 메서드가 호출되어 문자열로 변환된다.

Math.toString() // [object Math]
JSON.toString() // [object JSON]

유사하게 객체는 valueOf 메서드를 통해 숫자로 변환될 수 도 있다.

"J" + { toString: function(){ return "S"}}; //"JS"
2 * {valueOf:function(){return 3;}} ; //6

이처럼 객체가 toString(), valueOf() 메서드 둘다 포함할 경우 + 가 어떤 메서드를 호출하게 될지 명백하지 않다. 문자열 병합과 덧셈 연산이 데이터 형에 따라 선택되는데, 암묵적인 강제 형변환으로는 데이터형이 실제로 주어지지 않기 때문이다.

자바스크립트는 보이지 않게 valueOf 메서드를 실행한후 toString을 실행하여 이런 불확실함을 해소한다.

var obj = {
	toString : function(){
		return "[object MyObject]";
	},
	valueOf : function(){
		return :17;
	}
}

"object : " + obj; // object : 17

보통 숫자로 강제 형변환 하는것보다 문자열로 강제 형변환하는것이 훨씬더 일반적이고 유용하다. 때문에 valueOf()의 문자열 표현ㅇ르 나타내지 않는다면 valueOf의 사용을 피하는게 좋다.

트루시니스

false로 처리되는 값이 7가지가 있다

  • false
  • 0
  • -0
  • “”
  • NaN
  • null
  • undefined

이들을 제외하고는 모두 true로 처리된다.

undefined를 정확히 구분할 때

function point(x,y)
	if(typeof x === "undefined"){
		x = 320;
	}
	if(!y){
		y = 240;
	}
return {x,y}
}

point(0,0) // 0,240

typeof를 사용해서 0 과 undefined를 정확히 구분할 수 있다.

x === undefined  // 이렇게 사용해도 좋다. 

기억해야 할점

  • 데이터형 에러는 암묵적인 강제 형변환에 의해 은밀하게 감춰질 수 있다.
    • 연산자는 인자의 데이터형에 따라 덧셈이나 문자열 병합으로 오버로딩된다.
  • 객체는 valueOf를 통해 숫자형으로 , toString을 통해 문자열로 강제 형변환된다.
  • valueOf메서드르 ㄹ가지는 객체는 반드시 valueOf를 통해 생성되는 숫자값의 문자열 표현을 생성하는 toString메서드를 구현해야 한다.
  • undefined 값을 테스트할 때 트루시니스를 사용하기 보다는 typeof를 사용하거나 undefined와 비교하는것이 좋다.
profile
frontend developer

0개의 댓글