형변환 — 자바스크립트가 몰래 값을 바꾼다
Article
자바스크립트에는 친절한 구석이 있다. 숫자와 문자열을 섞어 써도 에러를 내지 않고, 알아서 하나로 맞춰준다.
그 친절함이 바로 초보자를 가장 많이 울리는 지점이다.
console.log("1" + 1);
console.log("1" - 1);
- '11'
- 0
같은 값인데 연산자만 바꿨더니 결과가 완전히 달라졌다. 이번 강의는 이 현상을 파헤친다.
왜 이런 일이 벌어지나
"1"은 문자열이고 1은 숫자다. 둘은 서로 다른 타입이라 원래 연산할 수 없다. 그런데 자바스크립트는 에러를 내는 대신 한쪽을 다른 쪽 타입으로 바꿔버린다. 이걸 암묵적 형변환이라 부른다.
문제는 어느 쪽으로 바꿀지가 연산자마다 다르다는 것이다.
+
문자열이 이긴다
- 한쪽이 문자열이면 이어붙이기
"1" + 1→"11"1 + "1"→"11"- 덧셈과 연결을 겸하는 유일한 연산자
- * / %
숫자가 이긴다
- 무조건 숫자로 바꿔 계산
"1" - 1→0"6" * "2"→12- 바꿀 수 없으면
NaN
+만 유별나다. 덧셈이면서 동시에 문자열 연결이기도 해서, 문자열을 만나면 연결 쪽으로 기운다.
console.log(1 + 2 + "3");
console.log("1" + 2 + 3);
- '33'
- '123'
왼쪽부터 차례로 계산되기 때문에 결과가 갈린다.
- 01
1 + 2 를 먼저 한다
둘 다 숫자라 그냥 더한다. 결과는
3(숫자).1 + 2 → 3 - 02
3 + "3" 을 한다
한쪽이 문자열이니 숫자 3 을 문자열
"3"으로 바꿔 이어붙인다.3 + "3" → "33"
"1" + 2 + 3은 첫 단계부터 문자열이라 계속 이어붙어서 "123"이 된다.
명시적으로 바꾼다
자바스크립트가 알아서 바꾸게 두지 말고, 직접 바꾸는 게 안전하다.
console.log(Number("42"));
console.log(Number("42px"));
console.log(parseInt("42px"));
console.log(String(42));
console.log(Boolean(1));
- 42
- NaN
- 42
- '42'
- true
Number()는 통째로 숫자여야 하고, parseInt()는 앞에서부터 읽다가 숫자가 아니면 멈춘다. "42px" 같은 CSS 값을 다룰 때 유용하다.
몇 가지는 결과가 직관과 어긋난다. 외우기보다 필요할 때 찾아보는 편이 낫다.
| 원래 값 | Number() | Boolean() |
|---|---|---|
| "" (빈 문자열) | 0 | false |
| " " (공백) | 0 | true |
| "0" | 0 | true |
| null | 0 | false |
| undefined | NaN | false |
| [] | 0 | true |
| {} | NaN | true |
공백 한 칸 " "은 숫자로는 0인데 boolean으로는 true다. "0"도 문자열로서는 비어 있지 않으니 true다.
truthy 와 falsy
6강에서 예고한 부분이다. 조건문이나 &&, ||에 넣으면 자바스크립트는 값을 boolean으로 바꿔 판단한다.
거짓으로 취급되는 값은 딱 여섯 개뿐이다.
| falsy 값 | 무엇인가 |
|---|---|
| false | boolean 거짓 |
| 0 | 숫자 영 (-0 포함) |
| "" | 빈 문자열 |
| null | 일부러 비운 값 |
| undefined | 아직 없는 값 |
| NaN | 숫자가 아님 |
이 여섯 개만 외우면 된다. 나머지는 전부 truthy다.
if ([]) console.log("빈 배열은 참이다");
if ({}) console.log("빈 객체도 참이다");
if ("0") console.log("문자열 0 도 참이다");
if (-1) console.log("음수도 참이다");
- 빈 배열은 참이다
- 빈 객체도 참이다
- 문자열 0 도 참이다
- 음수도 참이다
== 를 쓰면 안 되는 이유
드디어 본론이다. 자바스크립트에는 같음을 확인하는 연산자가 둘이다.
==
동등 — 타입을 맞춘 뒤 비교
- 타입이 다르면 몰래 변환한다
- 변환 규칙이 복잡하고 예외가 많다
- 숙련자도 결과를 못 맞춘다
- 쓰지 않는다
===
일치 — 타입까지 같아야 참
- 타입이 다르면 바로 거짓
- 규칙이 하나뿐이라 예측된다
- 읽는 사람이 헷갈리지 않는다
- 항상 이걸 쓴다
==가 얼마나 이상한지 직접 본다.
console.log(1 == "1");
console.log(0 == "");
console.log(0 == false);
console.log("0" == false);
console.log(null == undefined);
console.log([] == false);
- true
- true
- true
- true
- true
- true
전부 true다. 빈 배열이 false와 같다는 결론에 이르면 이미 정상적인 프로그래밍이 아니다.
===로 바꾸면 전부 뒤집힌다.
console.log(1 === "1");
console.log(0 === "");
console.log(null === undefined);
console.log([] === false);
- false
- false
- false
- false
타입이 다르면 볼 것도 없이 거짓이다. 이해하기 쉽고, 놀랄 일이 없다.
NaN 은 자기 자신과도 다르다
마지막 함정이다.
console.log(NaN === NaN);
const result = Number("사과");
console.log(result === NaN);
- false
- false
NaN은 "숫자가 아닌 무언가"인데, 그런 것들이 서로 같다고 할 근거가 없다. 그래서 자기 자신과 비교해도 거짓이다.
값이 NaN인지 확인하려면 전용 함수를 쓴다.
const result = Number("사과");
console.log(Number.isNaN(result));
console.log(Number.isNaN("사과"));
- true
- false
Number.isNaN()은 진짜 NaN일 때만 참이다. 문자열 "사과"는 숫자로 바꿀 수 없을 뿐 그 자체가 NaN은 아니므로 거짓이다.
정리하면
- 타입이 다른 값을 연산하면 자바스크립트가 알아서 한쪽을 바꾼다.
+는 문자열 쪽으로,-*/는 숫자 쪽으로 기운다.- 입력창의 값은 항상 문자열이다. 계산 전에
Number()로 바꾼다. - falsy는
false0""nullundefinedNaN여섯 개뿐이고 나머지는 truthy다. 빈 배열도 참이다. ==는 쓰지 않는다. 항상===를 쓴다.NaN은 자기 자신과도 다르다.Number.isNaN()으로 확인한다.
여기까지가 값을 다루는 이야기다. 다음 강의부터는 그 값으로 길을 나누는 법, 조건문을 배운다.