为什么”[] == ![]“为true??让我们领略JS类型转换的风采

194 阅读8分钟

在绝大多数语言当中,类型转换都作为一种很重要的机制存在,而在JS这种动态类型的语言中则显得更为重要,毕竟要把用户的语言转化为计算机能够存储及理解的类型极为重要,而在其他的方面,类型转换通常发生在看不见的角落,主要作为一种执行机制存在。那就让我们来看看它在JS中是怎样的执行机制与重要作用吧。

类型转换

类型转换是编程中将变量从一种数据类型转换为另一种数据类型的过程,旨在适应不同操作需求、保证数据一致性并提升代码灵活性与安全性。在JS这种弱类型的语言中转换较之其他更为简单,但更重要,下面进入主题。

首先我们来看这样一道面试题:

[] == ![]

问题是这里是true还是false。

那么,猜也能猜到,为true

那么为什么?

那就让我们先来了解类型转换

原始值转原始值

转布尔

在JS中原始值转原始值,并没有特殊的机制,而是有着一套规律,我们先来看转布尔类型,请看下面这段代码。

let s = 's'
let n = 123
let f = false
let u = undefined
let nu = null

console.log(Boolean(s));
console.log(Boolean(''));
console.log(Boolean(n));
console.log(Boolean(0));
console.log(Boolean(-1));
console.log(Boolean(Infinity));
console.log(Boolean(NaN));
console.log(Boolean(u));
console.log(Boolean(nu));
console.log(Boolean(f));
console.log(Boolean());

Boolean()是布尔类型的构造函数,传入的东西会被转换为布尔类型。

对于字符串类型,只要不为空字符,都可转换为true,而''会转为false;对于数字类型,正常有值的类型(包括负数,正数,Infinity无穷数)都会被转换为true,而0以及NaN这两位无值无法定义的数字则会被判定为false;对于布尔类型,就不多说了,不发生转换,会是它原来的值;而undefined、null以及空都会转换为false。

我们来看执行结果: image.png 这就是原始值转布尔的一套规律。下面来看转数字;

转数字

来看下方代码:

console.log(Number('123'));
console.log(Number('abc'));
console.log(Number(''));
console.log(Number('a123'));
console.log(Number('1a1a1a'));
console.log(Number('0x01a'));
console.log(Number(null));
console.log(Number(undefined));
console.log(Number());

Number()是数字类型的构造函数,传入的东西会被转换为数字类型

对于原始值转数字,其实是比较简单,即纯数字就转换为数字,含有字母就转换为NaN,一种情况例外,即0x+十六进制数,则会被识别为十六进制而后转换为十进制数字输出;而null会被转为0,undefined会被转换为NaN,而空即默认返回0.

这里是执行结果: image.png

而转字符串,我就不细聊了,因为在JS中只要加上''就会是字符串类型,而String()这个构造函数就是这么干的。这就是原始值转原始值了,就是规律,大致了解即可,下面进入重头戏。

对象转原始值

JS复杂类型皆对象,而对象怎么转原始值的呢?听我细细道来

首先是转布尔,因为对象转布尔都是true。这里就不细讲了,更重要的是转数字及转字符串。

开始前,我们需要了解两个函数,valueOf() 以及 toString()函数

valueOf()

valueOf() 是一个在 JavaScript 中存在于大多数对象中的方法。它的主要作用是返回对象的原始值或者基本数据类型的值,这对于需要对象的基本值而非对象本身引用的场景非常有用。不同的内置对象对 valueOf() 的实现方式不同:

  • 原始类型包装对象(如 new Number(), new Boolean(), new String())的 valueOf() 方法会返回对应的原始值(number, boolean, string)。
  • 数值对象(Number)的 valueOf() 返回数值本身。
  • 布尔对象(Boolean)的 valueOf() 返回布尔值 true 或 false。
  • 字符串对象(String)的 valueOf() 返回字符串内容。
  • 日期对象(Date)的 valueOf() 返回自1970年1月1日以来的毫秒数。
  • 其他对象,如果它们没有覆盖 valueOf() 方法,默认情况下返回对象本身(这在进行隐式类型转换时可能不如预期)。
toString()

toString() 是 JavaScript 中的一个方法,几乎每个对象都继承了它。这个方法的主要功能是将对象转换成字符串表示形式。对于不同的对象类型,toString() 的具体行为会有所不同:

  • 原始类型值(如数字、字符串、布尔值)的 toString() 方法会返回它们的字符串表示形式。例如,5..toString() 返回 "5"true.toString() 返回 "true"

  • 对象toString() 默认行为通常是返回一个表示对象类型的字符串,如"[object Object]",但对于一些内建对象,它会有特定的实现:

    • 数组(Array):返回数组元素的逗号分隔的字符串形式,例如 ["a", "b", "c"].toString() 返回 "a,b,c"
    • 日期(Date):返回日期的字符串表示,如 (new Date()).toString() 返回当前日期和时间的完整字符串。
    • 函数(Function):返回函数的源代码(函数体)。

转数字

还是调用Number()这个构造函数,但执行流程不尽相同,让我们来看看,这个函数究竟是怎么执行的:

1. 首先Number()会调用ToNumber(x)

2.该函数中会再调用ToPrimitive()将对象转为原始值

对于ToPrimitive()函数,它会接受两个参数,第一个是你传入的变量,第二个是你要转换的类型,对于此处 就是ToPrimitive(obj,Number),而这个函数会进行以下操作:

1. 判断接收到的值是不是原始类型,是则返回

2. 否则,调用valueOf方法,如果得到了原始值,则返回

3. 否则,调用toString方法,如果得到了原始值,则返回

4. 否则,报错

而结果就是如下:

  1. Object.prototype.toString() 返回一个like“[object xxxx]”的字符串
  2. Array.prototype.toString() 返回一个由数组内部的元素以逗号拼接的字符串
  3. 其他.prototype.toString() 返回一个字符串字面量

如果走到最后,就是一个字符串转数字,就是原始值转原始值,大都是NaN,除了数组转数字。

image.png

转字符串

与转数字差不多,只不过会少一步字符串转数字,而是直接输出字字符串,即:

image.png

我也就不多说了

我们再来看看,JS在我们不知道的地方悄悄的进行的类型转换:

计算

一元操作符 +

在绝大多数语言中,当+作为一元操作符的形式存在时,绝大多数语言就一个用途,即强调为正数

在JS中有些不同,除了这个功能外,还有一个功能,下面我们来看这一个代码

console.log(+{});

+会触发隐式类型转换,往number转,即:

image.png

我们给出的是{},也就会输出Number({})的结果->>NaN.

二元运算符 +

  1. 算术加法:当 + 运算符用于两个数值(整数或浮点数)之间时,它执行的是算术加法操作。例如,5 + 3 结果为 8
  2. 字符串连接:如果 + 运算符用于两个字符串之间,那么它会将这两个字符串拼接成一个新的字符串。例如,"Hello, " + "world!" 结果为 "Hello, world!"

在JS中,+ 还有一个特殊的行为,那就是在涉及非字符串与字符串的操作时,非字符串会被转换成字符串然后再进行连接。例如,5 + " apples" 会先将数字5转换为字符串 "5",然后与 " apples" 拼接,结果为 "5 apples"

== vs ===

== (等于)
在JS中,== 是一个相等运算符,用于比较两个操作数的值是否相等,不考虑它们的类型,即均会转换为数字类型,然后进行比较。即:

image.png

=== (严格等于/恒等)
=== 是一个严格相等或恒等运算符,它不仅比较操作数的值,还比较它们的类型,即不会转换类型。如果两个操作数的值和类型都完全相同,结果才是 true。即:

image.png

简单来说,就是:

  1. == 会触发类型转换
  2. === 不会

还需要注意的一点是,在计算中=的优先级总是最低的,不论是==或是===

通过这些对于类型转换的了解,我们大致就可以知道为何[] == ![]会为true了,我来详细解说一下

[] == ![]

首先 ==号双方都会转成数字

  1. ![] : 首先,! 是逻辑非操作符,用于将后面的表达式的结果转换为布尔值并取反。[],即空数组,在布尔上下文中会被视为 true(因为所有的对象在JS中作为布尔值都是 true),因此 ![] 的结果就是 false,然后false会转换为0
  2. [] 空数组转数字,即Number([]),过程不多讲,结果就是0
  3. 则最后的结果就是 0 == 0,自然为true

结语

希望我的文章对你有所帮助,点个赞再走呗,你的赞对我很重要,( •̀ ω •́ )✧!!!!