Skip to content

JavaScript 类型转换

导航目录

强制类型转换

parseInt 和 parseFloat

核心概念

  • parseInt 从左向右提取有效数字
  • parseFloat 解析浮点数
  • 第二个参数指定进制(2-36)
javascript
parseInt("10"); // 10
parseInt("10px"); // 10
parseInt("n10px"); // NaN

parseInt(27.2, 0); // 27 (基数为0时当作10进制处理)
parseInt(0, 1); // NaN (基数必须在2-36之间)
parseInt("0013", 2); // 1 (二进制001 -> 1)
parseInt("14px", 3); // 1 (三进制1 -> 1)
parseInt(123, 4); // 27 (4进制: 1*16 + 2*4 + 3*1 = 27)

一元加号运算符

核心概念

  • 将值转换为数字类型
  • BigIntSymbol 会报错
javascript
console.log(+null); // 0
console.log(+undefined); // NaN
console.log(+"123aa"); // NaN
console.log(+{}); // NaN
console.log(+true); // 1

// BigInt 和 Symbol 不能使用一元加号
console.log(+10n); // TypeError
console.log(+Symbol("a")); // TypeError

布尔值转换规则

关键规则

核心概念

  1. 假值(falsy):在布尔上下文中被视为 false 的值,包括:

    • false
    • 0-0NaN
    • ""(空字符串)
    • null
    • undefined
  2. 真值(truthy):除上述假值之外的所有值,在布尔上下文中都被视为 true

简单来说:所有对象(包括空对象和空数组)在布尔上下文中都被视为 true

if 语句中的假值

应用场景:避免因假值导致的逻辑错误

javascript
const result = {};
const obj = { name: 0 };

// 错误方式:0 会被转换为 false
if (obj.name) {
  result.name = obj.name;
}

// 正确方式:使用 hasOwnProperty 检查属性存在性
if (obj.hasOwnProperty("name")) {
  result.name = obj.name;
}

逻辑非运算符

核心原理! 将值转换为布尔类型并取反

javascript
console.log(!0); // true
console.log(!!0); // false
console.log("" === false); // false (类型不同)
console.log(!"" === !false); // true (都被转换为布尔值)

相等比较(==)的隐式转换

特殊规则

核心概念

  1. nullundefined 相等
  2. NaN 不等于任何值(包括自身)
  3. 布尔值会先转换为数字
  4. 字符串和数字比较,字符串转为数字
  5. 对象与原始值比较,对象转为原始值
  6. 对象与对象比较引用地址
javascript
console.log(null == undefined); // true
console.log(null == 0); // false
console.log("0" == false); // true (false→0, '0'→0)
console.log([] == ![]); // true (![]→false→0, []→''→0)
console.log("000" == 0); // true
console.log([true] == 1); // false ([true]→'true'→NaN)

加法运算符(+)的转换规则

二元加法规则

核心概念

  1. 如果有操作数是对象,先转为原始值
  2. 如果有一个操作数是字符串,执行字符串拼接
  3. 否则,将两个操作数转为数字进行加法运算
javascript
console.log([] + []); // '' (数组转字符串)
console.log([] + {}); // '[object Object]'
console.log({} + []); // '[object Object]' (Node.js) | 0 (浏览器控制台)
console.log({} + {}); // '[object Object][object Object]'

对象到原始值的转换机制

转换过程

核心概念

  1. 检查是否存在 Symbol.toPrimitive 方法
  2. 没有则根据提示(hint)调用:
    • "number": 先 valueOf()toString()
    • "string": 先 toString()valueOf()
    • "default": 同 "number" :::
javascript
const obj = {
  [Symbol.toPrimitive](hint) {
    console.log(`Hint: ${hint}`);
    if (hint === "number") return 10;
    if (hint === "string") return "hello";
    return true;
  },
};

console.log(+obj); // Hint: number → 10
console.log(`${obj}`); // Hint: string → 'hello'
console.log(obj + 5); // Hint: default → true + 5 = 6

默认 valueOf 和 toString

javascript
// Array
[1, 2].valueOf(); // [1, 2] (数组本身)
[1, 2].toString(); // "1,2"

// Object
{}.valueOf(); // {} (对象本身)
{}.toString(); // "[object Object]"

// Date
const now = new Date();
now.valueOf(); // 时间戳 (数字)
now.toString(); // 可读日期字符串

对象比较的特殊性

核心概念

  • 对象比较的是引用地址,不会进行隐式类型转换
  • 不会调用 Symbol.toPrimitivevalueOftoString
javascript
const obj1 = { value: 10 };
const obj2 = { value: 10 };

console.log(obj1 == obj2); // false (不同引用)
console.log(obj1 === obj2); // false
console.log(obj1 != obj2); // true

复杂隐式转换案例分析

案例 1:数组和对象的组合

解析:了解对象到原始值的转换过程

javascript
const arr = [4, 10];
arr[Symbol.toPrimitive] = function (hint) {
  return hint;
};
arr.valueOf = function () {
  return this;
};

const obj = {};

// 转换步骤:
// 1. +arr → "number" (字符串)
// 2. "number" + obj → "number[object Object]"
// 3. "number[object Object]" + arr → "number[object Object]default"
// 4. "number[object Object]default" + obj → "number[object Object]default[object Object]"

console.log(+arr + obj + arr + obj);
// 输出: "number[object Object]default[object Object]"

案例 2:数组和布尔值的转换

解析:理解 == 运算符的隐式转换规则

javascript
// 解析: [] == ![]
// 1. ![] → false (布尔值)
// 2. false → 0 (数字)
// 3. [] → "" → 0 (数字)
// 4. 0 == 0 → true

const val = [] == ![]; // true

// 解析: [1, 1] == [1, "1"]
// 数组比较引用地址,不是内容
console.log([+val, [] + 1] == [1, "1"]); // false

案例 3:对象和空数组的转换

解析:掌握复杂类型转换的步骤

javascript
// 步骤解析:
// 1. +{} → NaN
// 2. +[] → 0
// 3. (NaN + []) → "NaN" (字符串拼接)
// 4. "NaN"[0] → "N"

const val = (+{} + [])[+[]];
console.log(val); // "N"

类型转换总结表

转换类型规则说明
数字转换null→0, undefined→NaN, 字符串解析数字,true→1, false→0
布尔转换假值(null,undefined,0,NaN,'')→false, 其他 →true
字符串转换数组 → 元素逗号分隔,对象 →[object Object]null→"null", undefined→"undefined"
对象转换优先使用Symbol.toPrimitive,然后根据 hint 调用valueOf/toString

最佳实践建议

核心要点

  • 使用严格相等(===):避免隐式类型转换带来的意外结果
  • 显式转换:使用 Number(), String(), Boolean() 等显式转换方法
  • 理解假值:注意 0, '', null, undefined, NaN 在布尔上下文中的行为
  • 对象比较:使用 JSON.stringify() 或自定义比较函数比较对象内容
  • 避免复杂转换:尽量简化表达式,避免依赖复杂的隐式转换规则

实际应用场景

1. 表单输入处理

应用场景:将用户输入的字符串转换为数字

javascript
// 从表单获取输入值
const inputValue = "123.45";

// 显式转换为数字
const num = Number(inputValue);
console.log(num); // 123.45

// 处理空输入
const emptyInput = "";
const emptyNum = Number(emptyInput);
console.log(emptyNum); // 0

2. 条件判断优化

应用场景:避免因假值导致的逻辑错误

javascript
// 错误方式:0 会被视为 false
function processValue(value) {
  if (value) {
    return value * 2;
  }
  return 0;
}

// 正确方式:检查值是否存在
function processValue(value) {
  if (value !== undefined && value !== null) {
    return value * 2;
  }
  return 0;
}

console.log(processValue(0)); // 0
console.log(processValue(5)); // 10

3. 对象到原始值的自定义转换

应用场景:为自定义对象实现特殊的转换行为

javascript
class Temperature {
  constructor(celsius) {
    this.celsius = celsius;
  }

  [Symbol.toPrimitive](hint) {
    if (hint === "string") {
      return `${this.celsius}°C`;
    }
    if (hint === "number") {
      return this.celsius;
    }
    return this.celsius;
  }

  valueOf() {
    return this.celsius;
  }

  toString() {
    return `${this.celsius}°C`;
  }
}

const temp = new Temperature(25);
console.log(+temp); // 25 (数字转换)
console.log(`${temp}`); // "25°C" (字符串转换)
console.log(temp + 10); // 35 (加法运算)

总结

核心要点

  • 类型转换是 JavaScript 的重要特性,理解其规则对于编写可靠的代码至关重要
  • 隐式转换可能导致意外结果,建议使用严格相等(===)显式转换
  • 对象转换遵循特定的优先级:Symbol.toPrimitive > valueOf > toString
  • 布尔转换中,只有假值被视为 false,其他值都被视为 true
  • 最佳实践:显式转换、使用严格相等、理解假值、简化表达式

JavaScript 的类型转换机制虽然复杂,但掌握其规则可以帮助我们编写更健壮、更可预测的代码。通过合理使用显式转换和严格相等运算符,可以避免大多数因类型转换导致的问题。