JavaScript 是弱类型语言,会在运算时自动进行类型转换。理解这些规则,能帮你避开很多坑。
🎯 两种转换方式#
- 显式转换:开发者主动调用转换函数
- 隐式转换:运算符或语句自动触发
// 显式转换const num = Number('42') // 42const str = String(42) // "42"const bool = Boolean(1) // true
// 隐式转换const num2 = '42' - 0 // 42const str2 = 42 + '' // "42"const bool2 = !!'hello' // true转为数字#
Number() 函数#
// 字符串Number('42') // 42Number('3.14') // 3.14Number('42px') // NaN(无法完全解析)Number('') // 0Number(' ') // 0(空白字符串)
// 布尔值Number(true) // 1Number(false) // 0
// null 和 undefinedNumber(null) // 0Number(undefined) // NaN
// 对象Number({}) // NaNNumber([]) // 0(空数组)Number([1]) // 1(单元素数组)Number([1, 2]) // NaN(多元素数组)parseInt 和 parseFloat#
// parseInt:解析整数parseInt('42') // 42parseInt('42.9') // 42(截断小数)parseInt('42px') // 42(能解析前面的数字)parseInt('px42') // NaN(开头不是数字)parseInt(' 42 ') // 42(忽略空白)
// 指定进制parseInt('ff', 16) // 255parseInt('1010', 2) // 10parseInt('077', 8) // 63
// 🔶 不指定进制的坑parseInt('08') // 8(现代浏览器)// 旧浏览器可能解析为八进制的 0
// parseFloat:解析浮点数parseFloat('3.14') // 3.14parseFloat('3.14.15') // 3.14(到第一个无效字符停止)parseFloat('314e-2') // 3.14(支持科学计数法)隐式转数字#
// 一元 + 运算符;+'42' + // 42 true + // 1 null + // 0 undefined + // NaN [] + // 0 {} // NaN
// 减法、乘法、除法'10' - 5 // 5'10' * 2 // 20'20' / 4 // 5
// 比较运算符(非相等)'10' > 5 // true('10' 转为 10)'10' > '5' // false(字符串比较)转为字符串#
String() 函数#
String(42) // "42"String(3.14) // "3.14"String(true) // "true"String(false) // "false"String(null) // "null"String(undefined) // "undefined"String([1, 2, 3]) // "1,2,3"String({ name: 'test' }) // "[object Object]"String(Symbol('id')) // "Symbol(id)"toString() 方法#
const num = 42num.toString() // "42"num.toString(2) // "101010"(二进制)num.toString(16) // "2a"(十六进制)
const bool = truebool.toString() // "true"
const arr = [1, 2, 3]arr.toString() // "1,2,3"
// 🔶 null 和 undefined 没有 toString 方法// null.toString() // TypeError// undefined.toString() // TypeError隐式转字符串#
// 加号连接42 + '' // "42"true + '' // "true"null + ''[(1, 2)] + // "null" '' // "1,2" // 模板字符串 `value: ${42}` // "value: 42" `value: ${null}` // "value: null" `value: ${[1, 2]}` // "value: 1,2"转为布尔值#
Boolean() 函数#
只有 6 个假值,其余都是真值:
// 假值(Falsy)Boolean(false) // falseBoolean(0) // falseBoolean(-0) // falseBoolean(0n) // false(BigInt 零)Boolean('') // falseBoolean(null) // falseBoolean(undefined) // falseBoolean(NaN) // false
// 真值(Truthy)Boolean(true) // trueBoolean(1) // trueBoolean(-1) // trueBoolean('0') // true(非空字符串)Boolean('false') // true(非空字符串)Boolean([]) // true(空数组)Boolean({}) // true(空对象)Boolean(function () {}) // true隐式转布尔#
// 双重否定!!'hello' // true!!0 // false!!null // false!![] // true
// 条件语句if ('hello') console.log('真值')if (0) console.log('不会执行')
// 逻辑运算'hello' && 'world' // "world"(都是真值)'' && 'world' // ""(第一个是假值)'hello' || 'world' // "hello"(第一个是真值)'' || 'world' // "world"对象转原始值#
对象转原始值遵循 ToPrimitive 规则。
转换流程#
// 转数字时的调用顺序:valueOf() → toString()const obj = { valueOf() { console.log('valueOf') return 42 }, toString() { console.log('toString') return 'hello' },}
console.log(+obj) // valueOf → 42console.log(`${obj}`) // toString → "hello"(字符串上下文优先 toString)自定义转换#
// Symbol.toPrimitive 优先级最高const obj = { [Symbol.toPrimitive](hint) { console.log(`hint: ${hint}`) if (hint === 'number') return 42 if (hint === 'string') return 'hello' return true // default },}
console.log(+obj) // hint: number → 42console.log(`${obj}`) // hint: string → "hello"console.log(obj + '') // hint: default → "true"常见对象转换#
// 数组console.log([1, 2, 3] + '') // "1,2,3"console.log(+[]) // 0([] → "" → 0)console.log(+[1]) // 1([1] → "1" → 1)console.log(+[1, 2]) // NaN([1,2] → "1,2" → NaN)
// 对象console.log({} + '') // "[object Object]"console.log(+{}) // NaN
// Date 对象特殊:默认转字符串const date = new Date()console.log(date + '') // "Tue Dec 03 2025 ..."console.log(+date) // 1733212800000(时间戳)相等比较的类型转换#
== 的转换规则#
// 1. 相同类型:直接比��5 == 5 // true'hello' == 'hello' // true
// 2. null 和 undefined:相互相等null == undefined // truenull == null // trueundefined == undefined // true
// 3. 数字和字符串:字符串转数字5 == '5' // true('5' → 5)0 == '' // true('' → 0)
// 4. 布尔和其他:布尔先转数字true == 1 // true(true → 1)false == 0 // true(false → 0)true == '1' // true(true → 1,'1' → 1);(((false == ''[// 5. 对象和原始值:对象转原始值 // true(false → 0,'' → 0) 1]) == (1)['1']) == // true([1] → "1" → 1) (1)[(1, 2)]) == // true '1,2' // true经典陷阱#
// 让人困惑的相等console.log([] == false) // trueconsole.log([] == ![]) // true!// [] → "" → 0// ![] → false → 0// 0 == 0 → true
console.log(null == false) // false(null 只等于 undefined)console.log(undefined == false) // false
// 更多陷阱console.log(' \t\n' == 0) // true(空白字符串转为 0)console.log('0' == false) // trueconsole.log('0' == 0) // trueconsole.log(false == 0) // true// 但是console.log('0' == false) // trueconsole.log('0' == '') // false!加法的类型转换#
加法是最复杂的运算符,规则如下:
// 规则1:有字符串则连接'hello' + 'world' // "helloworld"'hello' + 42 // "hello42"42 + 'hello' // "42hello"
// 规则2:无字符串则转数字相加true + true // 2true + false // 1null + 1 // 1undefined + 1 // NaN
// 规则3:对象先转原始值[1, 2] + [3, 4] // "1,23,4"(都转为字符串){} + [] // 0({}被解析为空块,+[] = 0);({}) + [] // "[object Object]"[] + {} // "[object Object]"
// 经典题目console.log(1 + '2' + 3) // "123"console.log(1 + 2 + '3') // "33"console.log('1' + 2 + 3) // "123"实战技巧#
安全的类型转换#
// 转数字function toNumber(value) { const num = Number(value) return Number.isNaN(num) ? 0 : num}
// 转整数function toInteger(value) { return Math.floor(Number(value)) || 0}
// 转字符串(处理 null/undefined)function toString(value) { if (value == null) return '' return String(value)}
// 转布尔(明确真假值)function toBoolean(value) { return value != null && value !== '' && value !== 0 && value !== false}判断空值#
// 判断是否为空(null 或 undefined)function isNullish(value) { return value == null // 利用 == 的特性}
// 判断是否为假值function isFalsy(value) { return !value}
// 判断是否为空白字符串function isBlank(value) { return typeof value === 'string' && value.trim() === ''}
// 判断是否为空对象function isEmptyObject(obj) { return Object.keys(obj).length === 0}
// 判断是否为空数组function isEmptyArray(arr) { return Array.isArray(arr) && arr.length === 0}默认值处理#
// || 的问题:0 和空字符串被视为假值function getPort(config) { return config.port || 3000 // port 为 0 时会返回 3000}
// ?? 更安全function getPortSafe(config) { return config.port ?? 3000 // port 为 0 时返回 0}
// 复杂默认值function getConfig(options) { return { host: options?.host ?? 'localhost', port: options?.port ?? 3000, debug: options?.debug ?? false, }}常见面试题#
🙋 [] == ![] 为什么是 true?#
// 步骤分解:// 1. ![] 先执行,[] 是真值,![] = false// 2. [] == false// 3. [] 转原始值:[].toString() = ""// 4. "" == false// 5. 两边转数字:0 == 0// 6. 结果:true🙋 + [] 和 [] + 的区别?#
// {} + []// {} 被解析为空代码块// +[] 是一元加操作,[] → "" → 0// 结果:0
// [] + {}// 这是二元加法// [] → ""// {} → "[object Object]"// 结果:"[object Object]"
// 强制 {} 为表达式;({}) + [] // "[object Object]"🙋 如何让 a == 1 && a == 2 && a == 3 为 true?#
// 方案1:自定义 valueOfconst a = { value: 1, valueOf() { return this.value++ },}console.log(a == 1 && a == 2 && a == 3) // true
// 方案2:使用 getterlet _a = 1Object.defineProperty(window, 'a', { get() { return _a++ },})console.log(a == 1 && a == 2 && a == 3) // true
// 方案3:数组的 joinconst b = [1, 2, 3]b.join = b.shiftconsole.log(b == 1 && b == 2 && b == 3) // true总结#
| 转换目标 | 常用方法 | 隐式触发场景 |
|---|---|---|
| 数字 | Number() +x | 算术运算、比较运算 |
| 字符串 | String() "" | + 连接、模板字符串 |
| 布尔 | Boolean() !! | 条件判断、逻辑运算 |
核心要点���
- 记住 6 个假值:
false、0、""、null、undefined、NaN ==会触发类型转换,===不会- 对象转原始值:
Symbol.toPrimitive>valueOf>toString - 使用
??替代||处理null/undefined