前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库数组相等 类型转换 宽松相等
JavaScript值与类型

为什么 [] == ![] 是 true,而 [] == [] 是 false?

![] 先算:空数组是对象,对象都是真值,取反得到 false。然后 [] == false 按宽松相等规则,布尔值转数字得 0,数组转原始值得 '' 再转数字也是 0,所以相等。[] == [] 两边都是对象,只比较是不是同一个对象,两个字面量是两个不同的数组。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#值与类型
先看核心答案读代码示例
理解线索

`[] == ![]` 的三步转换

  1. ToBoolean![] 中空数组为真,取反得 false
  2. 布尔转数字[] == false 变成 [] == 0
  3. 对象转原始值[] 经 toString() 变成 '',再转数字得 0

宽松相等从头到尾不会把对象转成布尔值,所以「空数组是真值」和「空数组等于 false」可以同时成立。

核心回答

先记住这个答案

[] == ![] 要先算右边。! 会把操作数转成布尔值,空数组是对象,对象都是真值,所以 ![] 是 false,式子变成 [] == false。宽松相等遇到布尔值时会先把它转成数字,得到 [] == 0。一边是对象、一边是数字,对象要转成原始值,空数组的 toString() 是 '',得到 '' == 0。字符串再转数字,'' 是 0,最后 0 == 0 为 true。[] == [] 就简单了,两边都是对象时 == 不做任何转换,只比较是不是同一个对象,两个 [] 字面量各自创建了一个新数组,所以是 false。

  • 空数组是真值,![] 是 false
  • == 遇到布尔值,先把布尔值转成数字
  • 对象和数字比较时,对象先转原始值
  • 两个对象比较只看引用,不做任何转换

一步步拆开

把每一步的中间结果都打印出来,推导过程就一目了然。

中间结果和几个变体JavaScript
console.log(![])                      // 第一步
console.log([] == false, [] == 0)     // 第二、三步
console.log(JSON.stringify(String([])), '' == 0) // 数组转原始值、字符串转数字
console.log([] == ![], [] == [])

const a = []
console.log(a == a, a == [])          // 同一个引用才相等

console.log({} == !{})                // 对象换成 {}
console.log([0] == false, [1] == true, [2] == true)
查看输出与解释
false
true true
"" true
true false
true false
false
true true false

第三行的 "" 就是空数组转出来的空字符串。最后两行是面试常见的变体。{} == !{} 为 false,因为 !{} 是 false,转数字得 0;而 {} 转原始值是 '[object Object]',转数字是 NaN,NaN 不等于任何值。[2] == true 为 false,因为 true 先变成 1,[2] 变成 '2' 再变成 2,2 == 1 不成立。

宽松相等的转换优先级

== 的规则看起来多,其实可以按优先级记成几句话。两边类型相同,直接比较,对象就比引用。null 和 undefined 只和彼此相等。有布尔值,先把布尔值转数字。数字和字符串,字符串转数字。对象和原始值,对象转原始值(先试 valueOf,拿不到原始值再用 toString)。转完之后类型还不一样,就回到上面继续比。

对照这个顺序看 [] == ![],每一步只用到其中一条规则。关键是意识到 ! 是单独的运算,它用 ToBoolean,而 == 本身从不把对象转成布尔值。

回答前,多想一步

容易答错的地方

以为 == 会把数组转成布尔值再比较
如果是这样,[] 应该转成 true,[] == false 就该是 false。实际上 == 遇到布尔值是把布尔值转数字,对象走的是转原始值的路线。
以为 [] == [] 会比较内容
== 和 === 在两边都是对象时行为一样,比较的都是引用。想比较数组内容要自己逐项比,或者用 lodash 的 isEqual,不能用 JSON.stringify 一把梭,它会忽略 undefined、把 NaN 变成 null。
漏掉 ! 的优先级
[] == ![] 里 ! 优先级高于 ==,先算的是 ![],而不是 !([] == [])。
试着用自己的话回答

面试官还会怎么问?

怎么让 a == 1 && a == 2 && a == 3 成立?

利用对象转原始值时会调用 valueOf:定义 const a = { n: 1, valueOf() { return this.n++ } },每次 == 比较都会调用一次,依次返回 1、2、3。对象和数字比较时,先把对象转成原始值,再和数字比较。

[] + [] 和 [] == '' 分别是什么?

[] + [] 是空字符串,两个数组都转成 '' 再拼接。[] == '' 是 true,数组转原始值就是 ''。

项目里还有必要记这些规则吗?

写代码时统一用 ===,配合 ESLint 的 eqeqeq 规则,基本碰不到这些情况。记规则的意义在于读老代码、排查奇怪的比较结果时能快速推出原因。唯一常见的例外是 value == null,它能同时判断 null 和 undefined。

从一道题,走向一组知识

把知识连起来

值与类型

用 == 比较字符串和数字时,转换规则是什么?

[] == ![] 的最后一步是 '' == 0,也就是字符串和数字比较时字符串先转数字。

值与类型

null == undefined 为什么是 true?== 和 === 对空值的处理有什么不同?

了解了 == 有多绕,就明白为什么团队规范一律用 ===,只把 == null 留作例外。

参考资料

  • Equality (==) - MDN

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 一步步拆开
  3. 宽松相等的转换优先级
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑