JavaScript 类型转换完全指南:从入门到精通

2 阅读1分钟

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 值就够了: false0""nullundefinedNaN。除了这些,全是 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"NaNfalse
null"null"0false
true"true"1-
false"false"0-
""-0false
"123"-123true
"abc"-NaNtrue
0"0"-false
NaN"NaN"-false
[]""0true
[1]"1"1true
[1,2]"1,2"NaNtrue
{}"[object Object]"NaNtrue

四、面试真题来一波

题目一:[] == ![]

console.log([] == ![]);  // ?

一步一步来:

  1. ![][] 是 truthy,取反得 false
  2. 现在比的是 [] == false
  3. [] 转数字 → 0false 转数字 → 0
  4. 0 == 0true

答案: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.10.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 里一半的坑你就避开了。觉得有帮助的话,点赞收藏走一波,有问题评论区见!


参考资料: