面试官:[] == ![],返回什么?为什么?

2,647 阅读6分钟

引言

大家好,今天我们要谈论的是JavaScript中那些让人眼花缭乱的类型转换操作。在日常编程中,我们常常需要处理不同类型之间的转换,比如将数字转换为字符串、将布尔值转换为数字等等。而JavaScript中的类型转换机制又相当灵活,让人有时候啼笑皆非。废话不多说,让我们一起来揭开JavaScript类型转换的神秘面纱吧!

[] == ![] 将在讲述完原理后进行详细讲解。

原始类型互转

首先,让我们来看看JavaScript中的原始类型互转。在JavaScript中,原始类型包括数字、字符串、布尔值、null、undefined等。有时候我们需要将这些原始类型进行转换,这部分比较简单,看下表记住就好了。

参数类型初始值Number(xx)String(xx)Boolean(xx)
UndefinedundefinedNaN"undefined"false
Nullnull0"null"false
Booleantrue1"true"true
Booleanfalse0"false"false
NumberNaNNaN"NaN"false
NumberInfinityInfinity"Infinity"true
Number00"0"false
Number-123-123"-123"true
Number123123"123"true
String"123"123"123"true
String"-123"-123"-123"true
String""0""false
String" "0" "true

原始类型转对象

接下来,我们来看看原始类型如何转换为对象。在JavaScript中,原始类型可以通过包装对象的方式转换为对应对象。

new Boolean()

new Number()

new String()

对象转原始类型(重点)

除了原始类型之间的转换,JavaScript还支持对象转原始类型的操作。对象转原始类型时,JS会调用对象的valueOf() 方法和toString() 方法和 ToPrimitive 来进行转换。所以我们先来了解一下这三种方法的原理。

valueOf()

仅用于包装类对象,返回包装对象的原始值

image.png

toString()

toString()方法有多个,JS引擎会根据不同情况使用不同的toString()方法。

  1. {}.toString() 得到由"[object 和 class 和 ]" 组成的字符串,也就是'[object Object]'
  2. [].toString() 返回由数组内部元素以逗号拼接的字符串
  3. "xx".toString() 返回字符串字面量

ToPrimitive

那么ToPrimitive是什么呢,这个时候你点击Annotated ES5官方文档,它就会跳转到下面这个页面:

image.png

这个时候官方文档又要你跳转到8.12.8(下图):

image.png

好长好长,其实总结下来就是:

ToPrimitive(obj,String) ==> String(obj)

  1. 如果接收到的是原始值,直接返回值
  2. 否则,调用toString 方法,如果得到原始值,返回
  3. 否则,调用valueOf 方法,如果得到原始值,返回
  4. 否则,报错

ToPrimitive(obj,Number) ==> String(obj)

  1. 如果接收到的是原始值,直接返回值
  2. 否则,调用valueOf 方法,如果得到原始值,返回
  3. 否则,调用toString 方法,如果得到原始值,返回
  4. 否则,报错

对象转String

从JS官方文档Annotated ES5(下图)可以知道,对象转String会先调用ToPrimitive(input argument, hint String) 方法,然后将它得到的结果再利用ToString(上方表格内规则)转化为String类型。

image.png

注意ToString与toString是不一样的

对象转Number

对象转Number与对象转String类似,只是ToPrimitive调用valueOf 和toString 方法的顺序不同。

image.png

对象转Boolean

对象转Boolean就比较简单了,根据Annotated ES5官方文档可知,所有对象转Boolean都是true

image.png

一元运算符 +

对于一元运算符 +,它的执行原理与ToNumber是一致的,也就是对象转Number。

+[]

以+[]为例,它的转换过程是这样的:

首先调用ToPrimitive(obj,Number)方法:

  1. [].valueOf() 返回[],不是原始值
  2. [].toString() 返回''(空字符串) 再然后调用ToNumber,得到0

image.png

二元运算符 +

对于一元运算符 +,它的执行原理是:

v1 + v2 流程:

  1. lprim = ToPrimitive(v1) rprim = ToPrimitive(v2)
  2. 只要lprim 和 rprim 有一个是字符串,则非字符串使用ToString转为字符串,再直接拼接
  3. 否则,ToNumber(lprim) + ToNumber(rprim)

ToPrimitive依照Number顺序规则

null + 1

它的转换过程是这样的:

  1. null 和 1 都是原始数据类型,所以直接返回
  2. null 和 1 都不是字符串所以执行 ToNumber(null) + ToNumber(1)
  3. 所以变为 0 + 1 ,返回 1

[] + {}

它的转换过程是这样的:

  1. lprim = ToPrimitive(v1) rprim = ToPrimitive(v2), 得到lprim='',rprim = '[object Object]'
  2. 有字符串拼接,所以返回 '[object Object]'

== 操作符

如果你查阅Annotated ES5官方文档,是这样的:

image.png

总结下来就是,如果是相同类型

类型条件结果
UndefinedType(x) 和 Type(y) 都是 Undefined返回 true
NullType(x) 和 Type(y) 都是 Null返回 true
Number
x 和 y 都是 NaN返回 false
x 和 y 是相同的数值返回 true
x 是 +0,y 是 −0返回 true
x 是 −0,y 是 +0返回 true
其他情况返回 false
Stringx 和 y 是完全相同的字符序列(长度相同且对应位置的字符也相同)返回 true
Booleanx 和 y 都是 true 或都是 false返回 true
相同对象x 和 y 引用相同的对象返回 true

不同类型

类型条件结果
null 和 undefinedx 是 null 且 y 是 undefined返回 true
undefined 和 nullx 是 undefined 且 y 是 null返回 true
Number 和 Stringx 是 Number 而 y 是 String返回 x == ToNumber(y)
String 和 Numberx 是 String 而 y 是 Number返回 ToNumber(x) == y
Booleanx 是 Boolean返回 ToNumber(x) == y
Booleany 是 Boolean返回 x == ToNumber(y)
String 或 Number 和 Objectx 是 String 或 Number 而 y 是 Object返回 x == ToPrimitive(y)
Object 和 String 或 Numberx 是 Object 而 y 是 String 或 Number返回 ToPrimitive(x) == y
其他情况返回 false

[] == ![] 解读

下面回到我们的题目[] == ![]返回什么?为什么?

  1. ! 的优先级高于 == ,所以!先执行:
  2. !规则为先转化为Boolean 再取反,而[]为对象,所有对象转化为Boolean都为true,所以得到 [] == false
  3. 按照规则,类型不同,y为Boolean先进行x == ToNumber(y),得到 [] == 0
  4. 按照规则,类型不同,一边为Object,一边为Number,进行 ToPrimitive(x) == y,得到 '' == 0
  5. 按照规则,类型不同,一边为String,一边为Number, 进行 ToNumber(x) == y ,得到 0 == 0
  6. 最后返回 true

结语

通过今天的分享,相信大家对JavaScript中的类型转换有了更深入的了解。类型转换虽然有时候让人摸不着头脑,但正是这种灵活的类型转换机制,让JavaScript编程变得更加有趣和富有挑战性。希望大家在日常编程中能灵活运用类型转换的知识,让代码更加简洁高效!如果你觉得这篇文章有帮助或有所启发,别忘了给我一个鼓励的