这些奇怪的JavaScript隐式转换你一定遇到过!

一、JS 数据类型

JS 有 7 种基本类型:

  • number
  • string
  • boolean
  • null
  • undefined
  • symbol(ES6)
  • bigint(ES2020)

以及引用类型:object(包括数组、函数、日期等)

⚠️ 注意:typeof null === 'object' 是历史 bug,但 null 仍是原始类型。

二、隐式转换的三大核心规则

这些规则是 ECMAScript 规范中定义的抽象操作(abstract operation),JS 引擎在执行某些操作时会自动调用它,开发者不能直接调用例如 ToPrimitive(obj, 'string') 的操作,除非手动实现。

1. ToPrimitive(input, preferredType?)

将对象转为原始值。

  • 如果 preferredType"string",优先调用 toString()valueOf()
  • 如果是 "number"(或未指定),优先调用 valueOf()toString()
const obj = {
  valueOf() { return 42; },
  toString() { return 'hello'; }
};

console.log(obj + 1);      // 43 → 先 valueOf()
console.log(String(obj));  // "hello" → 先 toString()

📌 数组、Date 等内置对象也有默认的 valueOf/toString 行为。

但它是隐式转换,我们如何知道 preferredType ​传递的是谁呢?

其实,在规范的底层早就为某些操作做好了参数传递,只不过这个过程在我们开发者眼中是个“黑盒”。

preferredType 告诉引擎“你希望转成字符串还是数字?”,这个希望我们可以理解为这种规范:

当然,情况比我们想得更复杂,如果只是希望通过面试,掌握最基本的就足够了,希望更深度学习可以看官方文档: https://262.ecma-international.org/#sec-toprimitive

2. ToString(x)

将任意值转为字符串:

类型 转换结果
null "null"
undefined "undefined"
true "true"
42 "42"
[] ""(空数组 → 空字符串)
[1,2] "1,2"
{} "[object Object]"
Symbol('id') TypeError(不能转字符串)
'' + 42        // "42"
'' + true      // "true"
'' + null      // "null"
'' + [1,2]     // "1,2"

3. ToNumber(x)

将任意值转为数字:

类型 转换结果
"" 0
"42" 42
"hello" NaN
true 1
false 0
null 0
undefined NaN
[] 0(先转 "" → 再转 0)
[1] 1
[1,2] NaN("1,2" → 非法数字)
{} NaN
+""           // 0
+"42"         // 42
+true         // 1
+[]           // 0
+[1,2]        // NaN

三、总有面试官喜欢问这些

虽然我作为面试官从来不问这些问题,但有人他就喜欢问,咱能有什么办法。

场景 1:加法运算符 +

  • 如果任一操作数是字符串,则另一操作数转为字符串,执行拼接。
  • 否则,都转为数字相加。
1 + "2"        // "12" (字符串拼接)
1 + 2          // 3
"1" + true     // "1true"
[] + []        // "" ([] → "","" + "" → "")
{} + []        // "[object Object]"({} 被视为代码块,浏览器环境实际是 +[] → 0,但 REPL(node.js环境) 中常显示为对象字符串)
               // 但是如果是赋值的话,a = {} + []  // 只有一个结果 "[object Object]"
[] + {}        // "[object Object]"

💡 经典面试题:[] + {} vs {} + [] 结果不同,因 {} 在语句开头被解析为代码块。

场景 2:相等运算符 ==(宽松相等)

== 会进行类型转换(规则见;https://262.ecma-international.org/#sec-abstract-equality-comparison):

常见规则:

  1. null == undefinedtrue
  2. string == number → 字符串转数字
  3. boolean 与任何类型比较 → 先转为 number
  4. 对象 vs 原始值 → 对象先 ToPrimitive
42 == "42"        // true
true == 1         // true(true → 1)
false == 0        // true
null == undefined // true
[] == 0           // true([] → "" → 0)
![] == []         // true(![] → false,[] → 0,false == 0 → true)

最佳实践:工作中永远使用 ===(严格相等),避免隐式转换!

场景 3:逻辑运算符 &&, ||, !

  • !:先转为布尔值(ToBoolean),再取反
  • && / ||:返回操作数本身(短路求值),但判断依据是“真值性”

多提一嘴短路求值!

在很多语言(如 Java、C++)中,逻辑运算符总是返回 truefalse。 但在 JavaScript 中,它们返回的是参与运算的操作数本身(准确来说是决定“真值性” 的那个数)。

console.log(42 && "hello");   // "hello"
console.log(0 && "hello");    // 0
console.log("" || "default"); // "default"
console.log("foo" || "bar");  // "foo"

那么,什么叫做决定“真值性”呢?我以上面这几个例子说明:第一个逻辑与,42 是真值,但决定不了整个表达式的逻辑是真还是假,所以能决定“真值性”的是“hello”;第二个同理,0 直接决定了逻辑与就是假,所以可以直接返回;第三个逻辑或,第一个空串为假值,第二个值才能决定整体真假,所以返回“default”;第四个也同理,真假由“foo“就可以决定。

在 JavaScript 中,

falsy 值(转为 false)只有 7 个:

false
0, -0, 0n
"", ''(空字符串)
null
undefined
NaN

其余所有值都是 truthy(转为 true),包括:

"0", "false", [], {}, [0], function() {}, 42, -1, Infinity, new Date()

我们在公司中经常看到这样的代码:

// 安全调用函数
callback && callback();

// 安全访问数组元素
arr && arr.length > 0 && arr[0];

他们就是利用了“短路”求值的规则,保证的代码健壮性。

场景 4:关系运算符 <, >, <=, >=

  • 如果双方都是字符串 → 按字典序比较
  • 否则 → 都转为数字比较
"2" > "10"     // true(字典序:"2" > "1")
2 > "10"       // false(2 > 10 → false)
[2] > [10]     // false([2]→"2", [10]→"10" → 字符串比较?错!)
// 实际:[2] 和 [10] 都转为数字 → 2 > 10 → false

⚠️ 注意:[1,2] > [1]NaN,因为 "1,2" 转数字为 NaN

场景 5:一元运算符 +, -, ++, --

  • +x:尝试将 x 转为数字(ToNumber
  • -x:同上,再取负
+"42"    // 42
+true    // 1
+[]      // 0
+[1,2]   // NaN

场景 6:[] == ![]

[] == ![]  // true

// 分解:
![] → false
[] == false → 
  → [] 转原始值:"" 
  → false 转数字:0
  → "" 转数字:0
  → 0 == 0 → true
posted @ 2025-11-25 14:37  秀秀不只会前端  阅读(7)  评论(0)    收藏  举报