javascript 中, == 和 === 是有所不同的,== 号两侧的标记,只要经过类型转换之后可以相等,那么返回结果就会是true,而 === 则需要等号两侧完全相同,类型也必须相同,我们在开发过程中,通常会要求使用全等运算符(===),这样有助于我们写出更加可以预测行为的代码,但是我们也需要了解 == 的转换逻辑
== 转换逻辑可以用下图概括
==状态下判断结果的流程图,可以总结为以下四步
- undefined == null,结果为true,且他俩和所有其他值比较的结果都是 false
- String == Boolean,需要两个操作数同时转为 Number
- String/Boolean == Number,需要String/Boolean 转为 Number
- Object == Primitive,需要Object转为Primitive(通过valueOf 和 toString 方法)
- 如果Object为Date,则先调用toString,若toString的结果不是基本类型,再调用valueOf
- 如果Object为其他类型(obj、array、func),则先调用 valueOf,若结果不为基本类型,再调用 toString
- 基本类型:指 null、undefined、string、number、boolean
那么为什么 [] == ![] 结果为true呢
运算符优先级: ! > ==,所以先计算!(非运算符)
非运算符的运算规则,!toBoolean(oldValue),需要将[]先转为boolean值
boolean转换规则:
- Number:0、NaN为false
- undefined、null:false
- string:空字符串为false
- object:true
由于[]为object,所以toBoolean([])为true,取反,![] 结果为false
问题转换为:[] == false
- Object 和 Primitive 类型的比较,首先要把Object类型转换为Primitive类型
- []为普通object类型(不是date),所以先调用valueOf(),结果为[],仍然不是Primitive类型,无法比较
- 再调用[]的toString方法,[].toString() =’’,为字符串类型,结束
问题转换为:’’ == false
- string 和 boolean 进行比较,需要将两个值都转换为 number类型
- toNumber(‘’) = 0
- toNumber(false) = 0
- 所以结果为true
[] == [] 结果是什么
答:false,因为 array为引用类型,两个 [] 指向不同的内存地址
![] == {} 结果是什么
![]结果为false,{}在valueOf和toString后结果为”[Object Object]”,转换为Number为NaN,NaN与任何值比较的结果都是false
{} == ![] 结果是什么
报错:==(因为{}被认为是空代码块 而不是 对象Object)
js 语句优先,{} 有三个语义
- 复合语句块
- 对象直接量
- 声明函数块
理解顺序如上,所以{}首先被理解为 复合语句块
{} == !{} 结果是什么
false,【????】为什么没有语句优先
+ 运算符
https://segmentfault.com/a/1190000007184573
转换规则
1 | operand + operand = result |
- 使用 ToPrimitive 运算符转换 两个操作符为原始数据类型
- 转换结束之后,如果有运算元出现原始数据类型为“字符串”时,则另一运算元 强转为string,再进行字符串连接运算
- 其他情况,所有运算元都会转换为 数字 类型,然后做数字的相加运算
ToPrimitive 运算符
ToPrimitive(input, PreferredType?)
- preferredType :Number | String
- Number
- 若input为原始数据类型,直接返回input
- 当input是一个对象时,调用对象的valueOf() 方法,若能够得到原始类型的值,则返回这个值
- 否则,如果input调用valueOf之后仍然为一个对象,再调用input的toString() 方法,如果能得到原始数据类型的值,则返回这个值
- 否则,抛出 TypeError 错误(可能用户覆盖了object的toString 方法,导致这个方法 返回了一个对象而不是string)
- String(即Number类型的2、3步骤对调)
- input(原始数据类型直接输出)
- input为一个对象时,先调用toString,再调用valueOf(如Date对象就是这样的)
- 否则,抛出TypeError错误
- default:
- Date对象和Symbol对象的预设类型为 String
- 其他对象为 Number
- Number
- 一些特殊情况
- Object的toString,会输出该 object 的type “[object type]”,而Array的toString,会返回将数组用
join(',')
操作后返回的字符串- 如果想用toString来判断对象类型,可以通过 Object.prototype.toString.call(obj)来判断,因为Array的toString表现不同只是因为Array对象的prototype上覆写了toString方法
- Function 的 toString,返回 将整个函数转换为string的结果
- 一元正号(+),会让这个PreferedType 设置为 Number(如:+[] 的输出其实为 Number([]),输出0)
- Object的toString,会输出该 object 的type “[object type]”,而Array的toString,会返回将数组用
实例
【注】 undefined 转换为数字为 NaN,而 null 转换为数字为 0
[] + []
解析:
- toPrimitive([]) =>
- [].valueOf() = [] 不是原始类型
- [].toString() = ‘’为原始类型
- ‘’ + []
- 第二个 [] 同理,结果为 ‘’
{} + {}
解析:两种情况
- 第一个 {} 被当做对象处理(Chrome浏览器)
- toPromitive({}),valueof为{},toString为”[object object]”
- “[object object]” + {}
- 第二个 {} 直接toString
- 第一个 {} 被当做代码块处理(Firefox)
- 第一个 {} 代码块直接被忽略,原问题转化为 +{}
- Number({})为NaN
{} + []
不同浏览器表现相同,第一个 {} 都被当作 代码块处理,则结果为 Number([]),结果为0
【注】所有 {} + anyType 的运算,第一个{} 都会当作代码块而忽略,直接运行 Number(anyType)
几个神奇的转换
(! + [] + [] + ![]).length
!(+[]) + [] + ![]
!Number([]) + [] + ![]
true + [].valueOf() + ![]
true + [].toString() + ![]
true + ‘’ + false = ‘truefalse’.length = 9