JavaScript 类型转换完全指南:从入门到精通
说实话,类型转换这玩意儿,真的是让我当年学 JS 时掉了一大把头发。你有没有过这种经历——写
console.log('5' + 3)满心期待输出8,结果出来个"53",当场心态崩了?没关系,今天咱们把这个彻底搞明白。
一、为什么类型转换这么让人头疼?
JavaScript 是个动态类型语言,变量的类型可以随时变。这本来是好事,但有时候会搞出一些让人摸不着头脑的操作:
console.log('5' + 3); // "53" —— 字符串拼接
console.log('5' - 3); // 2 —— 数字运算
console.log([] + []); // "" —— 空字符串
console.log([] + {}); // "[object Object]"
console.log({} + []); // 0 或 "[object Object]"(跟上下文有关)
刚看到这些的时候,我整个人都是懵的。后来才知道,这些都是类型转换在搞鬼。搞懂了它,不仅写代码更稳,面试也不带怕的。
二、两种类型转换方式
2.1 显式转换——明明白白告诉你"我要转了"
说白了,显式转换就是你自己主动调方法,代码一读就知道要干什么,可读性拉满。
转成字符串
// String() 构造函数
String(123); // "123"
String(true); // "true"
String(null); // "null"
String(undefined); // "undefined"
String([1, 2, 3]); // "1,2,3"
String({a: 1}); // "[object Object]"
// toString() 方法(注意:null 和 undefined 没有这个方法!)
(123).toString(); // "123"
(true).toString(); // "true"
[1, 2, 3].toString(); // "1,2,3"
// 模板字符串(看着像显式,其实底层是隐式转换)
`${123}`; // "123"
转成数字
// Number() —— 严格转换
Number("123"); // 123
Number("12.34"); // 12.34
Number(""); // 0(空字符串变 0)
Number(" "); // 0(空格也会变 0)
Number("123abc"); // NaN
Number(true); // 1
Number(false); // 0
Number(null); // 0(null 也是 0)
Number(undefined); // NaN(这个要注意)
Number([]); // 0
Number([1]); // 1
Number([1, 2]); // NaN(多元素数组直接 NaN)
Number({}); // NaN
// parseInt() —— 只取整数部分
parseInt("123"); // 123
parseInt("123.45"); // 123(小数直接砍掉)
parseInt("123abc"); // 123(遇到字母就停)
parseInt("abc123"); // NaN(开头就不是数字,直接报废)
parseInt("0x10"); // 16(支持十六进制)
parseInt("10", 2); // 2(二进制转十进制)
parseInt("10", 8); // 8(八进制转十进制)
// parseFloat() —— 保留小数
parseFloat("123.45"); // 123.45
parseFloat("123.45abc"); // 123.45
// 一元加号 —— 偷懒写法
+"123"; // 123
+true; // 1
+false; // 0
+null; // 0
转成布尔值
// Boolean() 构造函数
Boolean(1); // true
Boolean(0); // false
Boolean("hello"); // true
Boolean(""); // false
Boolean([]); // true(空数组也是 true!没想到吧)
Boolean({}); // true(空对象也是 true!)
Boolean(null); // false
Boolean(undefined); // false
Boolean(NaN); // false
// 双重否定 —— 简写版
!!"hello"; // true
!!0; // false
!![]; // true
记住这 6 个 falsy 值就够了: false、0、""、null、undefined、NaN。除了这些,全是 true。包括 "0"、"false"、[]、{},全是 true!
2.2 隐式转换——JS 在背后偷偷帮你做了
有些操作你明明没写类型转换,但 JS 引擎自动给你加上转换了,这就是隐式转换。
算术运算符里的坑
// 加法 (+) 比较特殊,字符串优先级最高
1 + 2; // 3 —— 正常加法
"1" + 2; // "12" —— 变成字符串拼接了
1 + "2"; // "12" —— 同上
1 + 2 + "3"; // "33" —— 先算 1+2=3,再拼 "3"
"1" + 2 + 3; // "123" —— 先拼成 "12",再拼成 "123"
// 减乘除就不一样了——统一转数字
"5" - 3; // 2
"5" * 3; // 15
"6" / 2; // 3
"5" * "2"; // 10
// 一些有意思的case
[] + []; // "" —— 两个空数组转成字符串拼一起
[] + {}; // "[object Object]"
{} + []; // 0 —— 开头的 {} 被当成代码块了!
{} + {}; // NaN
比较运算符——== 和 === 到底啥区别?
// == 宽松相等,会自动转类型
1 == "1"; // true(字符串 "1" 转成数字 1)
1 == true; // true(true 转成 1)
0 == false; // true(false 转成 0)
0 == ""; // true(空字符串也转成 0)
null == undefined; // true(这俩是特殊兄弟)
[] == false; // true(数组转数字是 0)
[] == ""; // true(空数组和空字符串都是 0)
"0" == false; // true
// === 严格相等,不转类型,直接比
1 === "1"; // false(类型都不一样)
1 === true; // false
0 === false; // false
日常写代码,建议无脑用 === 和 !==,能省很多莫名其妙的问题。
逻辑运算符——其实返回的不是布尔值
// && 和 || 返回的是值本身,不是 true/false
"hello" && "world"; // "world"(第一个是真,返回第二个)
"" && "world"; // ""(第一个是假,直接返回)
"hello" || "world"; // "hello"(第一个是真,直接返回)
"" || "world"; // "world"(第一个是假,返回第二个)
// 常用写法:设置默认值
const name = userInput || "匿名用户"; // 如果 userInput 是空的,就用"匿名用户"
const value = data && data.info; // data 存在才去取 info
三、底层是怎么转的?
3.1 ToPrimitive——对象转原始值的内部机制
当 JS 引擎需要把对象转成原始值的时候,会调用一个内部方法叫 ToPrimitive。流程是这样的:
// 优先级:
// 1. 有 [Symbol.toPrimitive] 方法?用它
// 2. 没有的话,看 hint 是 "string" 还是 "number",决定先调 valueOf 还是 toString
const obj = {
[Symbol.toPrimitive](hint) {
if (hint === "number") return 42;
if (hint === "string") return "hello";
return true; // 默认情况
}
};
console.log(+obj); // 42(hint 是 number)
console.log(`${obj}`); // "hello"(hint 是 string)
console.log(obj + ""); // "true"(hint 是 default)
3.2 速查表
| 值 | 转字符串 | 转数字 | 转布尔 |
|---|---|---|---|
undefined | "undefined" | NaN | false |
null | "null" | 0 | false |
true | "true" | 1 | - |
false | "false" | 0 | - |
"" | - | 0 | false |
"123" | - | 123 | true |
"abc" | - | NaN | true |
0 | "0" | - | false |
NaN | "NaN" | - | false |
[] | "" | 0 | true |
[1] | "1" | 1 | true |
[1,2] | "1,2" | NaN | true |
{} | "[object Object]" | NaN | true |
四、面试真题来一波
题目一:[] == ![]
console.log([] == ![]); // ?
一步一步来:
![]→[]是 truthy,取反得false- 现在比的是
[] == false []转数字 →0,false转数字 →00 == 0→true
答案:true(很多人第一次都会猜错)
题目二:{} + {} 和 {} + []
console.log({} + {}); // ?
console.log({} + []); // ?
作为表达式时,{} 是对象:
{} + {}→"[object Object]" + "[object Object]"→"[object Object][object Object]"{} + []→"[object Object]" + ""→"[object Object]"
不过如果你在控制台直接敲,{} 开头会被解析成代码块,结果就完全不同了,这个细节面试经常考。
题目三:NaN 的那些坑
console.log(typeof NaN); // "number"
console.log(NaN === NaN); // false(NaN 连自己都不等于自己!)
console.log(isNaN("abc")); // true(会先把 "abc" 转成 NaN)
console.log(Number.isNaN("abc")); // false(不转类型,直接判断)
Number.isNaN() 比全局的 isNaN() 靠谱多了,因为全局那个会自动转数字。
题目四:0.1 + 0.2 !== 0.3
console.log(0.1 + 0.2 === 0.3); // false(不是类型转换问题,是精度问题)
console.log(0.1 + 0.2); // 0.30000000000000004
这个严格来说不算类型转换,但面试必问。原因是 JS 用 IEEE 754 双精度浮点数,0.1 和 0.2 都无法精确表示,加起来就变成了 0.30000000000000004。
五、实战建议
5.1 用 === 别用 ==
// ❌ 别这样
if (value == null) { ... } // 偶尔能跑,但容易踩坑
// ✅ 乖乖用严格相等
if (value === null || value === undefined) { ... }
5.2 显式转换比隐式转换香
// ❌ 隐式转换——看着简洁,但别人读代码容易懵
const num = +str;
const bool = !!value;
const str = val + "";
// ✅ 显式转换——一目了然
const num = Number(str);
const bool = Boolean(value);
const str = String(val);
5.3 判空要准确
// ❌ 别用 !value 判空——0、""、false 都会被误判
if (!value) { ... }
// ✅ 明确判断 null/undefined
if (value === null || value === undefined) { ... }
// ✅ 数组判空
if (arr.length === 0) { ... }
// ✅ 对象判空
if (Object.keys(obj).length === 0) { ... }
5.4 parseInt 要指定进制
// ❌ 容易出问题
parseInt("08"); // 某些老版本会当成八进制解析
// ✅ 养成好习惯
parseInt("08", 10); // 明确十进制
parseInt("10", 2); // 二进制
parseInt("FF", 16); // 十六进制
六、总结
| 场景 | 做法 |
|---|---|
| 比较相等 | === / !== |
| 转字符串 | String(val) |
| 转数字 | Number(val) 或 parseInt/parseFloat |
| 转布尔 | Boolean(val) 或 !!val |
| 判空 | val === null || val === undefined |
| 设默认值 | val ?? "默认值"(比 || 更安全,0 和 "" 不会触发) |
类型转换这块搞懂了,JS 里一半的坑你就避开了。觉得有帮助的话,点赞收藏走一波,有问题评论区见!
参考资料: