JS的转换规则
约 4152 字大约 14 分钟
2026-04-19
== 符的强制类型转换规则
3 个特殊结论
null==undefined:返回true(两者互为唯一的 “对等值”);- null/undefined 与其他任何类型的值(除了彼此)比较:返回 false(例如 null == 0、undefined == "" 均为 false);
- Symbol 类型与任何非 Symbol 类型的值比较:返回 false(例如 Symbol('test') == "Symbol(test)"、Symbol() == 123 均为 false)。
console.log(null == undefined); // true
console.log(null == 0); // false
console.log(undefined == ''); // false
console.log(Symbol('a') == Symbol('a')); // false(类型相同,比较引用,Symbol唯一)
console.log(Symbol('a') == 'Symbol(a)'); // false(类型不同,直接返回false)场景 1:布尔值(Boolean)与任意其他类型比较
转换规则:先将布尔值转为数字(true → 1,false → 0),然后再将转换后的数字与另一操作数按后续规则进行比较。
关键:布尔值永远是「被转换的一方」,不会让其他类型转为布尔值。
示例代码:
// 示例1:Boolean == Number
console.log(true == 1); // true(true→1,1==1)
console.log(false == 0); // true(false→0,0==0)
console.log(true == 2); // false(true→1,1≠2)
// 示例2:Boolean == String
console.log(true == '1'); // true(true→1,"1"→1,1==1)
console.log(false == ''); // true(false→0,""→0,0==0)
console.log(false == '0'); // true(false→0,"0"→0,0==0)
// 示例3:Boolean == 引用类型
console.log(true == []); // false(true→1,[]→0,1≠0)
console.log(false == []); // true(false→0,[]→0,0==0)场景 2:字符串(String)与数字(Number)比较
转换规则:将字符串转为数字(遵循 Number() 函数的严格转换规则),然后比较两个数字的值是否相等。
补充:空字符串、纯空格字符串转为
0,非数字格式字符串转为NaN,NaN与任何值比较均为false。
示例代码:
console.log('123' == 123); // true("123"→123,123==123)
console.log('123.45' == 123.45); // true("123.45"→123.45)
console.log('' == 0); // true(""→0,0==0)
console.log(' ' == 0); // true(纯空格字符串→0,0==0)
console.log('123a' == 123); // false("123a"→NaN,NaN≠123)
console.log('NaN' == NaN); // false("NaN"→NaN,NaN与自身不相等)场景 3:引用类型(Object/Array/Function 等)与原始类型(String/Number/Boolean)比较
转换规则:先将引用类型通过 ToPrimitive 抽象操作转为原始类型(底层核心流程:valueOf() → 若返回非原始类型则调用 toString()),然后将转换后的原始类型与另一操作数按上述规则进行比较。
简化理解:引用类型 → 先
valueOf()(通常返回自身,无效)→ 再toString()(转为字符串)→ 按需再转为数字(根据另一操作数类型)。
常见引用类型的转换结果:
- 空数组
[]:toString()→ ""(空字符串) - 非空单元素数组
[123]:toString()→ "123" - 多元素数组
[1,2,3]:toString()→ "1,2,3" - 普通对象
{}:toString()→ "[object Object]" - 函数
function(){}():toString()→ 函数源码字符串
示例代码:
// 示例1:Array == String
console.log([] == ''); // true([]→"",""=="")
console.log([123] == '123'); // true([123]→"123","123"=="123")
console.log([1, 2] == '1,2'); // true([1,2]→"1,2","1,2"=="1,2")
// 示例2:Array == Number
console.log([] == 0); // true([]→""→0,0==0)
console.log([123] == 123); // true([123]→"123"→123,123==123)
console.log([1, 2] == 12); // false([1,2]→"1,2"→NaN,NaN≠12)
// 示例3:Object == 原始类型
console.log({} == '[object Object]'); // true({}→"[object Object]")
console.log({ name: 'JS' } == ' [object Object]'); // false(转换后字符串不一致)场景 4:补充:BigInt 与 Number 比较
转换规则:直接比较数值是否相等,但若数值超出 Number 的安全范围(±2^53-1),则返回 false(避免精度丢失导致的误判)。 示例代码:
console.log(123n == 123); // true(数值相等,且在安全范围)
console.log(9007199254740991n == 9007199254740991); // true(Number 安全最大值)
console.log(9007199254740992n == 9007199254740992); // false(超出 Number 安全范围,精度丢失)易踩坑的经典示例(拆解转换过程)
[] ==  拆解步骤:- 第一步:
![]是逻辑非操作,先将[]转为布尔值([]是引用类型,转为true),再取反得到false; - 第二步:原式变为
[] == false,进入「布尔值与引用类型比较」场景; - 第三步:
false转为0,[]转为""再转为0; - 第四步:
0 == 0,返回true。
- 第一步:
{} == !{}(返回false) 拆解步骤:- 第一步:
!{}转为false,原式变为{} == false; - 第二步:
false转为0,{}转为 "[object Object]" 再转为NaN; - 第三步:
NaN == 0,返回false。
- 第一步:
JS 其他值转到字符串的转换规则
两个核心转换方法的差异
String()全局函数:支持所有类型的值(包括null、undefined),转换安全无报错,是通用转字符串方案;toString()实例方法:仅支持非null、非undefined的值(这两个类型无该方法,调用会抛出TypeError),部分类型(Number、BigInt)支持可选「基数参数」,可转为指定进制的字符串。
原始类型转字符串(直接转换,无复杂逻辑)
原始类型(String、Number、Boolean、Null、Undefined、Symbol、BigInt)转字符串,除 null、undefined 外,均遵循「字面量对应」原则,具体规则如下:
| 原始类型 | 转换规则 | 示例代码(String() 实现) |
|---|---|---|
| String 类型 | 直接返回字符串本身,无任何修改(包括空字符串、含特殊字符的字符串) | String("hello") // "hello"、String("") // "" |
| Number 类型 | 转为对应数字的字面量字符串,特殊数字(NaN、Infinity、±0)有固定对应结果 | String(123) // "123"、String(123.45) // "123.45"、String(NaN) // "NaN"、String(Infinity) // "Infinity"、String(-0) // "0" |
| Boolean 类型 | 固定映射:true → "true",false → "false" | String(true) // "true"、String(false) // "false" |
| Null 类型 | 固定返回 "null"(仅 String() 支持,null.toString() 报错) | String(null) // "null" |
| Undefined 类型 | 固定返回 "undefined"(仅 String() 支持,undefined.toString() 报错) | String(undefined) // "undefined" |
| Symbol 类型 | 转为 "Symbol(描述内容)" 格式(仅支持显式转换,隐式转字符串会抛错) | String(Symbol("test")) // "Symbol(test)"、String(Symbol()) // "Symbol()" |
| BigInt 类型 | 转为对应整数值的字面量字符串,末尾无 n 标识,特殊值 0n 转为 "0" | String(123n) // "123"、String(999999999999999999n) // "999999999999999999"、String(0n) // "0" |
补充:toString() 方法的特殊用法(Number/BigInt 支持基数)
Number 和 BigInt 类型的 toString() 可接收第二个参数(基数,范围 2-36),转为对应进制的字符串,这是 String() 不具备的功能:
// Number 转指定进制字符串
console.log((10).toString(2)); // "1010"(十进制 10 转二进制)
console.log((255).toString(16)); // "ff"(十进制 255 转十六进制)
// BigInt 转指定进制字符串
console.log(100n.toString(8)); // "144"(十进制 100 转八进制)引用类型转字符串(依赖 ToPrimitive 抽象操作,hint: string)
所有引用类型(Object、Array、Function、Date 等)转字符串,底层均依赖 JS 引擎的 ToPrimitive 抽象操作,且此时转换提示(hint)为 string(优先转为字符串类型的原始值),具体转换流程如下:
引用类型转字符串的核心流程(ToPrimitive + hint: string)
- 若当前值已是原始类型,直接返回(极少出现,引用类型本身非原始类型);
- 优先调用该对象的
toString()方法,若返回结果是「原始类型」(字符串、数字、布尔等),直接将该原始值转为字符串(完成转换); - 若
toString()返回的是引用类型,再调用该对象的valueOf()方法,若返回结果是「原始类型」,将该原始值转为字符串(完成转换); - 若
valueOf()仍返回引用类型,抛出TypeError(无法转为原始类型,实际开发中极少遇到)。
关键区别:引用类型转字符串时,
toString()优先级高于valueOf();而引用类型转数字时,valueOf()优先级高于toString()。
常见引用类型的具体转换示例(高频场景)
1. 数组(Array)
数组的 toString() 方法会将所有元素转为字符串,再用「逗号 ,」拼接,空数组直接返回空字符串:
// 示例1:空数组
console.log(String([])); // ""([] → toString() 返回 "",原始类型,直接转字符串)
// 示例2:单元素数组
console.log(String([123])); // "123"([123] → toString() 返回 "123")
console.log(String([null])); // "null"([null] → toString() 返回 "null")
// 示例3:多元素数组
console.log(String([1, 2, 3])); // "1,2,3"([1,2,3] → toString() 返回 "1,2,3")
console.log(String([1, [2, 3]])); // "1,2,3"(嵌套数组先递归转字符串,再拼接)
// 示例4:特殊元素数组
console.log(String([Symbol('test')])); // "Symbol(test)"2. 普通对象(Object,无自定义 toString())
普通对象的默认 toString() 方法固定返回 "[object Object]"(这是 Object 原型上的 toString() 实现,用于标识对象类型):
console.log(String({})); // "[object Object]"
console.log(String({ name: 'JS', age: 20 })); // "[object Object]"3. 日期对象(Date)
日期对象的 toString() 方法会返回一个「人类可读的标准日期时间字符串」,优先级远高于 valueOf()(valueOf() 返回时间戳数字),这是特殊场景:
const now = new Date();
console.log(String(now)); // 例如 "Tue Jan 13 2026 10:00:00 GMT+0800 (中国标准时间)"4. 函数(Function)
函数的 toString() 方法会返回该函数的「源码字面量字符串」,包括函数关键字、参数、函数体(格式化与定义时一致):
// 普通函数
console.log(
String(function fn(a, b) {
return a + b;
})
);
// "function fn(a, b) { return a + b; }"
// 箭头函数
console.log(String((x, y) => x * y));
// "(x, y) => x * y"
// 匿名函数
console.log(
String(function () {
console.log('hello');
})
);
// "function() { console.log("hello"); }"5. 正则表达式(RegExp)
正则对象的 toString() 方法会返回正则的「字面量字符串」,包括 / 分隔符和修饰符:
console.log(String(/abc/gi)); // "/abc/gi"
console.log(String(/^\d+$/)); // "/^\\d+$/"(转义字符会保留转义格式)6. 自定义 toString() 的对象(覆盖默认规则)
若自定义对象覆盖了 toString() 方法,会优先执行自定义方法(遵循 ToPrimitive 流程),这是修改转字符串结果的常用方式:
const person = {
name: '张三',
age: 25,
// 自定义 toString() 方法
toString() {
return `Person: ${this.name}(${this.age}岁)`;
}
};
console.log(String(person)); // "Person: 张三(25岁)"
console.log(person.toString()); // "Person: 张三(25岁)"(与 String() 结果一致)隐式转字符串的场景(遵循相同规则)
除了显式调用 String()/toString(),JS 引擎在某些场景下会自动触发隐式转字符串,底层仍遵循上述转换规则,常见场景:
+运算符其中一个操作数为字符串时(字符串拼接);- 模板字符串中嵌入变量(
${变量}会自动转字符串)。
示例代码:
// 场景1:+ 运算符字符串拼接
console.log(123 + '456'); // "123456"(123 隐式转为 "123")
console.log([] + 'abc'); // ""abc""([] 隐式转为 "")
// 场景2:模板字符串嵌入变量
const obj = { name: 'JS' };
console.log(`对象内容:${obj}`); // "对象内容:[object Object]"(obj 隐式转为字符串)JS 其他值到 boolean 值的转换规则是什么?
核心转换方法(显式转换,可控无歧义)
有两种常用且效果完全等价的显式转布尔值方法,可根据场景选择:
Boolean()全局函数:语义清晰,适合需要明确表达「类型转换」意图的场景;!!双重逻辑非运算符:语法简洁,无额外性能开销,是实际开发中更常用的简写方式(第一次!转为布尔值并取反,第二次!再取反还原为正确布尔值)。
示例代码:
// 两种方法效果完全一致
console.log(Boolean(123)); // true
console.log(!!123); // true
console.log(Boolean('')); // false
console.log(!!''); // false核心规则:只有 7 个「假值」转为 false,其余全为 true
JS 中转布尔值的规则极其简单,无需复杂记忆,只需牢记:仅有 7 个固定值会被转为 false,所有其他值(包括所有引用类型)均会被转为 true。
必须牢记的 7 个「假值」列表(完整无遗漏)
这是 JS 内置的固定假值,无额外扩展,具体如下:
undefined:未定义的值(变量声明未赋值、函数无返回值等场景);null:表示「空值」的特殊对象;false:布尔值本身的假值;0、+0、-0:所有数字形式的零(正数零、负数零无区别);NaN:非数字(Not a Number),注意NaN本身是数字类型,但转布尔值为false;""(空字符串):长度为 0 的字符串(无任何字符,包括无空格);0n:BigInt 类型的零(末尾带n的整数值零,其他 BigInt 均为真值)。
假值转换示例(均返回 false)
console.log(Boolean(undefined)); // false
console.log(!!null); // false
console.log(Boolean(0n)); // false
console.log(!!NaN); // false
console.log(Boolean('')); // false「真值」范围(除 7 个假值外,全部都是真值)
真值的范围无需逐一列举,只需记住「排除假值即为真值」,重点关注容易被误解的「看似假实则真」的场景:
- 所有非空字符串(包括
" "(含空格)、"false"、"0"等,只要长度 > 0 就是真值); - 所有非零数字(包括
1、-1、0.1、Infinity、-Infinity等); - 所有引用类型(Object 及其子类):包括空数组
[]、空对象{}、空函数function(){}、日期对象new Date()、正则对象/^$/等,无论是否「为空」,转布尔值均为true; - 所有 Symbol 类型(
Symbol()、Symbol("test")等,无例外); - 所有非
0n的 BigInt 类型(1n、-100n等)。
真值转换示例(均返回 true,重点关注易误解场景)
// 易误解的字符串真值
console.log(!!' '); // true(含空格,非空字符串)
console.log(Boolean('false')); // true(字符串内容为"false",非空)
console.log(!!'0'); // true(字符串内容为"0",非空)
// 所有引用类型均为真值(包括空引用)
console.log(Boolean([])); // true(空数组)
console.log(!!{}); // true(空对象)
console.log(Boolean(function () {})); // true(空函数)
console.log(!!new Date()); // true(日期对象)
// 其他真值
console.log(Boolean(1n)); // true(非0n的BigInt)
console.log(!!Symbol('test')); // true(Symbol类型)
console.log(Boolean(Infinity)); // true(无穷大数字)Object.is?
语法格式
Object.is(value1, value2);- 参数:
value1、value2(需要进行比较的任意两个值,支持所有原始类型和引用类型); - 返回值:布尔值
true/false(仅当两个值是「同一个值」时返回true,否则返回false)。
核心特点
Object.is() 是严格相等判断的增强版,它既保留了 === 「不进行隐式类型转换」的严格性,又修复了 === 在两个特殊场景下的不合理判断,让相等判断更符合直觉。
Object.is() 完整判断规则
Object.is() 的判断逻辑分两步,优先级依次递减:
第一步:判断两个值的类型是否相同 若
value1和value2的类型不同(例如123和"123"、null和undefined),直接返回false(与===一致,无隐式类型转换)。第二步:类型相同时,分类型判断是否为「同一个值」
- 原始类型(String、Number、Boolean、Null、Undefined、Symbol、BigInt):判断「值是否完全一致」;
- 引用类型(Object、Array、Function 等):判断「是否指向同一个内存地址」(与
===一致,不比较对象内容,仅比较引用地址); - 特殊值处理(修复
===的缺陷,这是Object.is()的核心差异): ①NaN与NaN:返回true(===中NaN === NaN返回false,这是不合理的,Object.is()修正了该问题); ②+0(正零)与-0(负零):返回false(===中+0 === -0返回true,Object.is()能区分二者,符合底层数值逻辑)。
常见场景示例代码
// 1. 普通原始类型(与 === 结果一致)
console.log(Object.is(123, 123)); // true
console.log(Object.is('hello', 'hello')); // true
console.log(Object.is(null, null)); // true
console.log(Object.is(undefined, undefined)); // true
console.log(Object.is(Symbol('test'), Symbol('test'))); // false(Symbol 唯一,值不同)
console.log(Object.is(123n, 123n)); // true
// 2. 特殊值(与 === 结果不同,核心差异)
console.log(Object.is(NaN, NaN)); // true(修正 === 的缺陷,符合直觉)
console.log(Object.is(+0, -0)); // false(能区分正零和负零,底层数值逻辑更准确)
// 3. 引用类型(与 === 结果一致,比较内存地址)
const obj1 = { name: 'JS' };
const obj2 = obj1;
const obj3 = { name: 'JS' };
console.log(Object.is(obj1, obj2)); // true(指向同一个内存地址)
console.log(Object.is(obj1, obj3)); // false(内容相同,地址不同,不是同一个值)
console.log(Object.is([], [])); // false(两个空数组,地址不同)
// 4. 类型不同(与 === 结果一致,返回 false)
console.log(Object.is(123, '123')); // false
console.log(Object.is(null, undefined)); // falseObject.is() 的适用场景
Object.is() 并非要替代 ===,而是在特定场景下补充 === 的不足,推荐使用场景如下:
- 需要准确判断一个值是否为
NaN: 以往判断NaN只能用Number.isNaN(),现在也可以用Object.is(value, NaN),结果更直观且准确。
const num = Number('abc'); // NaN
console.log(Object.is(num, NaN)); // true(准确判断)- 需要区分
+0和-0的底层数值计算场景: 这是极少出现的特殊场景(多在底层数学运算、动画物理引擎等场景),===无法区分二者,而Object.is()可以精准区分。
const positiveZero = 0;
const negativeZero = -0;
console.log(Object.is(positiveZero, negativeZero)); // false(精准区分)- 需要「严格且无歧义」的相等判断: 当你需要避免
===的两个「不合理判断」,同时又不需要==的隐式转换时,Object.is()是最优选择,它的判断逻辑更符合「同一个值」的语义。