JS 类型转换
理解隐式和显式类型转换 · 难度:入门 · +10XP
JavaScript 类型转换完全解析
JavaScript 是弱类型(动态类型)语言,变量可以随时改变类型,运算符会自动触发隐式类型转换。类型转换分为两种:隐式转换(由 JS 引擎自动完成)和显式转换(开发者主动调用方法)。深入理解转换规则是避免诡异 bug 的关键——比如为什么 [] + [] 结果是空字符串,而 [] + {} 结果是 "[object Object]"。这些看似奇怪的行为,背后都有明确的转换规则。
一、转换为字符串(ToString)
将其他类型转换为字符串是最常见的类型转换之一。有四种主要方式,它们的结果不完全相同,特别是在处理 null 和 undefined 时需要特别注意。
| 方法 | 示例 | 结果 | 特殊说明 |
|---|---|---|---|
| String(val) | String(123) | "123" | 最安全,支持 null/undefined |
| val.toString() | (123).toString() | "123" | null/undefined 会报错! |
| val + "" | 123 + "" | "123" | 隐式转换,通过 + 触发 |
| 模板字符串 | ${123} | "123" | ES6 方式,自动调用 toString |
// 各类型转字符串的结果
console.log(String(42)); // "42"
console.log(String(true)); // "true"
console.log(String(false)); // "false"
console.log(String(null)); // "null"
console.log(String(undefined)); // "undefined"
console.log(String(NaN)); // "NaN"
console.log(String(Infinity)); // "Infinity"
// 数组和对象
console.log(String([1, 2, 3])); // "1,2,3"(去掉方括号,元素用逗号连接)
console.log(String({})); // "[object Object]"(没啥用的默认输出)
console.log(String({name:'A'})); // "[object Object]"(还是这个!)
// toString 的陷阱
console.log((42).toString()); // "42"
// null.toString(); // TypeError! null 没有 toString 方法
// undefined.toString(); // TypeError!
二、转换为数字(ToNumber)
字符串转数字有四大方法,它们的行为有重要区别。parseInt 和 parseFloat 更宽容(允许末尾存在非数字字符),而 Number() 和一元加号则要求纯净的数字字符串。
| 方法 | 行为 | "42px" | "3.14.15" | "" | null | undefined |
|---|---|---|---|---|---|---|
| Number() | 严格转换 | NaN | NaN | 0 | 0 | NaN |
| parseInt() | 提取开头整数 | 42 | 3 | NaN | NaN | NaN |
| parseFloat() | 提取开头浮点数 | 42 | 3.14 | NaN | NaN | NaN |
| +str | 同 Number() | NaN | NaN | 0 | 0 | NaN |
// parseInt 详解(第二个参数指定进制,强烈建议始终提供!)
console.log(parseInt('42px')); // 42(读到非数字字符为止)
console.log(parseInt('1010', 2)); // 10(把 "1010" 当作二进制解析)
console.log(parseInt('FF', 16)); // 255(把 "FF" 当作十六进制解析)
console.log(parseInt('077')); // 77(ES5+ 不再识别八进制前缀0)
// 经典陷阱:没有明确指定进制导致的问题
console.log(parseInt('08')); // 8(正常)
// 老引擎中可能被当作八进制解析为 0,所以永远传第二个参数!
// parseFloat 只处理十进制
console.log(parseFloat('3.14')); // 3.14
console.log(parseFloat('3.14.5')); // 3.14(只解析到第二个点之前)
console.log(parseFloat(' 3.14 ')); // 3.14(自动去除前后空格)
// Number() 的几个反直觉结果
console.log(Number('')); // 0(空字符串→0,容易出bug)
console.log(Number(' ')); // 0(纯空格也→0)
console.log(Number(null)); // 0(null→0)
console.log(Number(undefined)); // NaN(undefined→NaN!与null不同!)
三、转换为布尔值(ToBoolean)
在条件判断中,JavaScript 会自动将值转为布尔。记住:只有 6 个假值(falsy),其他所有值都是真值(truthy)。这 6 个假值是:false、0(含 -0)、""(空字符串)、null、undefined、NaN。特别注意:空数组 []、空对象 {}、包含空格的字符串 " " 都是 truthy!
// 六个假值 (falsy values) —— 其他所有值都是真值!
console.log(Boolean(false)); // false
console.log(Boolean(0)); // false
console.log(Boolean(-0)); // false
console.log(Boolean('')); // false(空字符串)
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean(NaN)); // false
// 容易误判的真值 (truthy values)
console.log(Boolean(' ')); // true —— 空格不等于空字符串!
console.log(Boolean('false')); // true —— 非空字符串就是 true!
console.log(Boolean('0')); // true —— 字符串"0"非空!
console.log(Boolean([])); // true —— 空数组也是 true
console.log(Boolean({})); // true —— 空对象也是 true
console.log(Boolean(-1)); // true —— 只有0是假值
console.log(Boolean(Infinity)); // true
// !! 双感叹号 —— 快速将任意值转为布尔
console.log(!!'hello'); // true
console.log(!!''); // false
console.log(!!0); // false
console.log(!![]); // true
四、隐式类型转换的常见陷阱
理解隐式转换是写出可靠 JavaScript 的关键。最核心的规则:加号 + 有双重身份(数值加法 vs 字符串连接),当任一操作数是字符串时,+ 执行字符串连接而非数值加法。比较运算符 == 会触发复杂的类型转换,推荐始终使用 === 严格相等。
// 加号 + 的歧义性 —— 字符串连接优先于数值加法
console.log(1 + '2'); // "12"(数字+字符串 = 字符串连接)
console.log(1 + 2 + '3'); // "33"(先1+2=3,再3+'3'='33')
console.log('1' + 2 + 3); // "123"('1'+2='12','12'+3='123')
console.log(5 + null); // 5(null转数字为0)
console.log('5' + null); // "5null"(字符串+null=字符串连接)
console.log(5 + undefined); // NaN(undefined转数字为NaN)
console.log(5 + true); // 6(true转数字为1)
console.log(5 + false); // 5(false转数字为0)
// == 宽松相等 vs === 严格相等 —— 永远优先使用 ===
console.log('5' == 5); // true(字符串转数字后相等)
console.log('5' === 5); // false(类型不同,直接返回false)
console.log(false == 0); // true(false转数字为0)
console.log(false === 0); // false
console.log(null == undefined); // true(特殊的语言设计)
console.log(null === undefined); // false(类型不同)
console.log('' == 0); // true(空字符串转数字为0)
console.log('' === 0); // false
// 奇怪的加号和数组
console.log([] + []); // ""(转换为空字符串再拼接)
console.log([] + {}); // "[object Object]"
console.log({} + []); // "[object Object]" 或 0(取决于代码块上下文)
- 安全类型检测与转换:编写 toNumberSafe(val) 函数,尝试将任何值安全地转为数字:能转就转,转换失败返回 0。编写 toBooleanSafe(val) 明确返回假值列表之一的就返回 false,否则 true。再编写 toStringSafe(val) 安全转字符串,null/undefined 转为空字符串。
- 表单数据清洗:编写 cleanFormData(form) 函数,接收 {name, age, score} 对象。name 去除首尾空格且不为空;age 转为整数且在 0-150 之间;score 转为浮点数且在 0-100 之间。不合法字段设为默认值。
- 真假值实验:对以下值依次用 Boolean() 检查并记录结果:[0, -0, '', ' ', [], {}, null, undefined, NaN, 'false', '0', Infinity, -Infinity]。写一个循环输出每个值的真假结果,并与6个假值列表对照验证。
- 等号对比实验:比较下面每组用 == 和 === 的结果,解释差异原因:('0' vs false), ('' vs 0), ([] vs 0), ([1] vs 1), ([1,2] vs '1,2'), (null vs undefined), (null vs 0), (undefined vs 0)。
- 安全计算器:编写 safeCalculate(a, op, b) 函数,将 a 和 b 安全转为数字后,根据 op('+'、'-'、'*'、'/')执行计算。除数为零、非数字等异常情况返回友好的错误消息而非 NaN 或 Infinity。