JavaScript 类型转换详解

小飞兽 JavaScript 420 次阅读 2026-04-25

JavaScript 类型转换详解

Introduction

JavaScript 是一门弱类型语言,变量在赋值时可以动态改变类型,而在表达式求值过程中,类型转换也无处不在。理解类型转换的规则,是写出健壮 JavaScript 代码的基础。无论是字符串与数字的拼接、条件判断中的隐式转换,还是 ===== 的差异,背后都涉及 JavaScript 的类型转换机制。稍有疏忽,就会出现诸如 "1" + 2 结果为 "12" 而非 3 这样的"奇怪"现象。本文将系统讲解 JavaScript 类型转换的两大类别——隐式转换与显式转换,从底层原理到日常实践,帮助你彻底掌握这一核心知识点。

基础语法

一、显式类型转换(Explicit Type Conversion)

显式转换指开发者主动调用转换函数,明确告知 JavaScript 进行类型转换。

1. 转为字符串

String(123);         // "123"
String(true);        // "true"
String(null);        // "null"
String(undefined);   // "undefined"
String({a: 1});      // "[object Object]"
String([1, 2, 3]);   // "1,2,3"

// 或使用 .toString()
(123).toString(); // "123"
true.toString(); // "true"

2. 转为数字

Number("123");       // 123
Number("12.34");     // 12.34
Number("");          // 0
Number("  42  ");    // 42(自动忽略首尾空格)
Number("abc");       // NaN
Number(true);        // 1
Number(false);       // 0
Number(null);        // 0
Number(undefined);   // NaN
Number({});          // NaN

// parseInt / parseFloat(更宽松,从左到右解析)
parseInt("42px"); // 42
parseInt("3.14km"); // 3
parseFloat("3.14km"); // 3.14
parseInt("abc"); // NaN

3. 转为布尔值

Boolean(1);         // true
Boolean(0);         // false
Boolean("");        // false
Boolean("abc");     // true
Boolean(null);      // false
Boolean(undefined); // false
Boolean({});        // true(对象永远为 true)
Boolean([]);        // true(空数组也为 true)

4. 转为整数或浮点数(进阶)

parseInt("42", 10); // 42(十进制)
parseInt("101", 2); // 5(二进制转十进制)
parseInt("0xFF", 16); // 255(十六进制)

Number("42.5"); // 42.5
Math.floor(42.9); // 42
Math.round(42.5); // 43
Math.ceil(42.1); // 43
Math.trunc(42.9); // 42(直接截断)

二、隐式类型转换(Implicit Type Conversion / Coercion)

隐式转换由 JavaScript 引擎在特定操作中自动触发,常见于运算符求值过程中。

1. 加法运算符 + 的字符串偏置

"5" + 3;       // "53"(数字被转为字符串)
5 + "3";       // "53"
"5" + true;    // "5true"
"5" + null;    // "5null"
"5" + undefined; // "5undefined"

2. 减法、乘法、除法运算符 — 转数字

"6" - 2;       // 4(字符串转为数字)
"6" * "3";     // 18
"6" / "2";     // 3
"abc" - 1;     // NaN
true - false;  // 1(布尔转为数字)
null - 5;      // -5(null 转为 0)
undefined - 5; // NaN

3. 比较运算符

// 数字 vs 字符串:字符串转数字
"5" > 3;       // true
"10" < 9;      // false("10" 转为 10)

// 布尔 vs 其他:布尔转数字
true > 0; // true
false == 0; // true(宽松相等)
false === 0; // false(严格相等,类型不同)

// null 和 undefined
null == undefined; // true(宽松相等,特殊规则)
null === undefined; // false
null > 0; // false
null >= 0; // true(null 转为 0)

4. 宽松相等 == 的类型转换规则

// 常见 == 转换陷阱
"0" == false;     // true("0" 转数字 0,false 转数字 0)
"" == false;      // true(空字符串转 0)
[] == false;     // true(数组转字符串 "",再转 0)
[] == "";        // true
{} == false;     // false(对象转字符串 "[object Object]")
{} == "[object Object]"; // true

代码示例

示例 1:强制类型转换函数对比

const values = [0, 1, "", "hello", null, undefined, NaN, [], {}, Infinity];

console.log("--- Number() ---");
values.forEach(v => console.log("Number(" + JSON.stringify(v) + ") = " + Number(v)));

console.log("\n--- Boolean() ---");
values.forEach(v => console.log("Boolean(" + JSON.stringify(v) + ") = " + Boolean(v)));

示例 2:巧用隐式转换

// 利用 + "" 将任意值转为字符串
const num = 42;
const str = num + "";  // "42"

// 利用 - 0 将字符串转为数字
const strNum = "3.14";
const num2 = strNum - 0; // 3.14

// 利用 !! 将任意值转为布尔
const maybe = "maybe";
const bool = !!maybe; // true

// 利用 | 0 将浮点数转为整数(截断)
const pi = 3.14159;
const int = pi | 0; // 3

示例 3:parseInt 进制转换

// 二进制字符串转十进制
parseInt("1101", 2); // 13

// 十六进制字符串转十进制
parseInt("FF", 16); // 255

// 八进制字符串转十进制
parseInt("77", 8); // 63

// 处理带前缀的进制字符串
parseInt("0xFF"); // 255(自动识别十六进制)

示例 4:Symbol 不能转为数字

Number(Symbol("id"));   // TypeError: Cannot convert a Symbol value to a number
String(Symbol("id"));   // "Symbol(id)"
Boolean(Symbol("id"));  // true(Symbol 对象转为 true)

示例 5:BigInt 类型转换

// BigInt 不能与普通 Number 混合运算
Number(123n);    // 123
BigInt(123);     // 123n

// BigInt 整数除法会丢失小数
console.log(Number(1n / 3n)); // 0(丢失小数部分)

运行效果

--- Number() 转换结果 ---
Number(0) = 0
Number(1) = 1
Number("") = 0
Number("hello") = NaN
Number(null) = 0
Number(undefined) = NaN
Number(NaN) = NaN
Number([]) = 0
Number({}) = NaN
Number(Infinity) = Infinity

--- Boolean() 转换结果 ---
Boolean(0) = false
Boolean(1) = true
Boolean("") = false
Boolean("hello") = true
Boolean(null) = false
Boolean(undefined) = false
Boolean(NaN) = false
Boolean([]) = true(空数组为 true!)
Boolean({}) = true
Boolean(Infinity) = true

--- parseInt 进制转换 ---
parseInt("1101", 2) = 13
parseInt("FF", 16) = 255

--- 隐式转换示例 ---
"5" + 3 = "53"
"6" - 2 = 4
"abc" - 1 = NaN
true - false = 1
null - 5 = -5
!!"maybe" = true
3.14159 | 0 = 3

常见问题

Q1: == 和 === 的区别是什么?

===(严格相等)不进行类型转换,只有类型和值同时相等才返回 true==(宽松相等)会先进行类型转换,再比较值。由于隐式转换规则复杂且容易出错,推荐始终使用 ===,只在明确需要类型转换的场景下使用 ==。

Q2: NaN 是什么?如何判断 NaN?

NaN(Not a Number)表示"不是数字"的计算结果,但它本身类型仍然是 Number。NaN 是唯一一个与自身不相等的值。

NaN === NaN;           // false(坑!)
isNaN(NaN);           // true
isNaN("abc");         // true("abc" 被转数字)
Number.isNaN(NaN);    // true
Number.isNaN("abc");  // false(更精确,只有真正的 NaN 才返回 true)

Q3: null 和 undefined 的区别?

    • null:表示"已赋值为空",是一个被显式设置的对象引用。
    • undefined:表示"未定义",变量声明但未赋值,或对象属性不存在。
    let a;
    console.log(a);      // undefined(声明未赋值)
    const b = null;
    console.log(b);     // null(显式赋空)
    console.log(typeof null); // "object"(历史遗留 bug)
    

    Q4: 空数组 == 空字符串 == 0?

    Number([]);         // 0(空数组先 toString 为 "",再转数字)
    Number("");         // 0
    [] == "";           // true
    0 == "";            // true
    // 所以 [] == 0 == "",三个值宽松相等
    

    Q5: parseInt 和 Number 的区别?

    • Number():严格转换,整个字符串必须是合法的数字,否则返回 NaN。
    • parseInt/parseFloat():宽松转换,从左到右解析,遇到非数字字符停止。
    Number("42px");     // NaN
    parseInt("42px");   // 42
    parseFloat("3.14km"); // 3.14
    

    Q6: 为什么 typeof null 是 "object"?

    这是 JavaScript 早期的设计缺陷。在 JS 最初的实现中,对象的内部类型标签是 0,null 的指针正好是 0x00,因此被错误地识别为 object 类型。这个 bug 已经存在了 20+ 年,由于兼容性原因至今未被修复。

    Q7: 如何安全地进行类型转换?

    // 安全转数字:先判断是否为数字字符串
    function toNumber(value) {
      if (typeof value === "number" && !isNaN(value)) {
        return value;
      }
      if (typeof value === "string" && /^\s*-?\d+(\.\d+)?\s*$/.test(value)) {
        return Number(value);
      }
      return NaN;
    }
    

    // 安全转布尔
    const safeBool = !!value;

    延伸阅读

    • MDN - Equality (==, ===, Object.is):详细对比三种比较方式
    • You Don't Know JS - Types and Grammar(Kyle Simpson):深入剖析 JavaScript 类型系统的经典书籍
    • Why typeof null is "object":2ality 博客的历史分析
    • BigInt:ES2020 引入的新类型,用于处理超过 Number 安全整数范围的数值
  • Symbol:ES6 引入的原始数据类型,常用作对象属性键,不会被隐式转换