ToPrimitive == === > <

作者: 在幽幽暗暗反反复复中追问 | 来源:发表于2019-10-11 16:55 被阅读0次

参考《你不知道的JavaScript(中卷)》第四章

理解 ToPrimitive 操作 就能理解了JS 中的有点迷的 == 操作了

// 以下是有点奇怪的 == 
"0" == false; // true
false == 0; // true
false == ""; // true
false == []; // true
"" == 0; // true
"" == []; // true
0 == []; // true

ToPrimitive 操作

抽象操作 ToPrimitive(参见 ES5 规范 9.1 节)会首先(通过内部操作 DefaultValue,参见 ES5 规范 8.12.8 节)检查该值是否有 valueOf() 方法。
如果有并且返回基本类型值,就使用该值进行强制类型转换。如果没有就使用 toString() 的返回值(如果存在)来进行强制类型转换。
如果 valueOf() 和 toString() 均不返回基本类型值,会产生 TypeError 错误。

什么是 ToNumber

true 转换为 1, false 转换为 0。 undefined 转换为 NaNnull 转换为 0。ToNumber 对字符串的处理基本遵循数字常量的相关规则 / 语法。处理失败时返回 NaN(处理数字常量失败时会产生语法错误)。不同之处是 ToNumber 对以 0 开头的十六进制数并不按十六进制处理(而是按十进制)

== 和 ===

== 允许在相等比较中进行强制类型转换,而 === 不允许。
实际上 ===== 都会检查操作数的类型。区别在于操作数类型不同时它们的处理方式不同。

特别情况

NaN == NaN  // false
+0 == -0 // true

ES5 规范 11.9.3.1 的最后定义了对象(包括函数和数组)的宽松相等(==):两个对象指向同一个值时即视为相等,不发生强制类型转换。
在比较两个对象的时候, ===== 的工作原理是一样的

== 比较不同类型时发生了什么

显然会进行类型转换
基本数据类型都转为数字
对象类型进行 ToPrimitive 操作

转换的优先级是 布尔 > 字符串 > 对象

最终他们都会转为 数字 类型(因为基本数据类型都会转为数字类型,对象的转换优先级最低,轮到对象进行转换的时候,另外一个需要转换的操作数早就转为数字了,如果 ToPrimitive 操作返回的结果非数字,那么要进行 == 操作的两个操作数的类型依然不同, ToPrimitive 操作返回的结果还需要转为数字)

举个例子,布尔值和字符串比较:

false == "abc"  // false
// 转换的优先级是 布尔 > 字符串,所以 false 先转为数字得到 0
// 现在相当于比较  0 == "abc",操作数的类型还是不同,继续类型转换
// "abc" 转为数字,Number("abc")  得到 NaN
// 0 == NaN 结果 false 

布尔值和对象比较:

false == []  // true
// 转换的优先级是 布尔 > 对象,所以 false 先转为数字得到 0
// 现在相当于比较  0 == [],操作数的类型还是不同,继续类型转换
// [] 是对象,进行 ToPrimitive 操作,得到空字符,
// 现在相当于比较 0 == "",操作数的类型还是不同,继续类型转换
// 空字符是基本数据类型,转为数字,Number("")  得到 0
// 0 == 0 结果 true 

那么 null 和 undefined 呢?这里引出另外一个问题:包装类型(看最后)
他们不会进行转换
null 只会 == undefined 或者自身,undefined 同样

null == null        // true
null == undefined   // true
undefined == undefined  // true

对 [] 和 {} 呢?
以下代码不是说明 [] == {} 发生了什么,因为两个对象进行宽松或者严格相等时,不进行类型转换,两个对象指向同一个值时即视为相等

// 对象和基本数据类型进行宽松比较时,对象发生了什么?
// 当对象是 []
[].valueOf()  // 还是一个对象
[].toString()  // ''  一个空字符串
Number("")  // 0

// 当对象是 [2,3]
[2,3].valueOf()  // 还是一个对象
[2,3].toString()  // "2,3"
Number("2,3")  // NaN

// 当对象是 [null]
[null].valueOf()  // 还是一个对象
[null].toString()  // ""
Number([null])  // 0
/*
 也许你认为 [null].toString() 返回的不是 ""
 但是如果不这样处理的话又能怎样呢?
 有人也许会觉得既然 String(null) 返回 "null"
 所以 String([null]) 也应该返回 "null"。
 确实有道理,实际上这是 String([..]) 规则的问题。
 又或者根本就不应该将数组转换为字符串?
 但这样一来又会导致很多其他问题
*/

// 当对象是 {}
({}).valueOf()  // 还是一个对象
({}).toString() // "[object Object]"  
Number( "[object Object]")  // NaN

>, <, <=

这属于抽象关系比较:
比较双方首先调用 ToPrimitive,如果结果出现非字符串,就根据 ToNumber 规则将双方强制类型转换为数字来进行比较。

实际上 JavaScript 中 <= 是 “不大于” 的意思(a <= b 被处理为 b < a,然后将结果反转。)即 a <= b ,处理为 !(b < a)。

相等比较有严格相等,关系比较却没有“严格关系比较”(strict relational comparison)。也就是说如果要避免 a < b 中发生隐式强制类型转换,我们只能确保 a 和 b 为相同的类型,除此之外别无他法。

包装类型

基本数据类型:number、string、boolean 都有包装类型,这是为了让这些基本数据类型可以方便地调用一些常用的方法,比如 toString,valueOf 等等

但是 null 和 undefined 没有对应的包装类型,所以 null 和 undefined 不能够被封装(boxed)
Object(null) 和 Object() 均返回一个常规对象。

“拆封”,即“打开”封装对象(如 new String("abc")),返回其中的基本数据类型值("abc")。

以上说的和 == 有什么关系?
因为 == 中的 ToPromitive 强制类型转换也会发生拆封,这大概就是很多人错误地认为 == 不进行类型判断的原因(我猜的)

var a = "abc";
var b = Object( a ); // 和new String( a )一样
a === b; // false
a == b; // true

Object(null) 和 Object() 均返回一个常规对象,没法拆封。

var a = null;
var b = Object( a ); // 和Object()一样
a == b; // false
var c = undefined;
var d = Object( c ); // 和Object()一样
c == d; // false
var e = NaN;
var f = Object( e ); // 和new Number( e )一样
e == f; // false

相关文章

  • ToPrimitive == === > <

    参考《你不知道的JavaScript(中卷)》第四章 理解 ToPrimitive 操作 就能理解了JS 中的有点...

  • 面试题合集

    a == 1 && a == 2 && a == 3 true a[Symbol.toPrimitive] ...

  • [JavaScript] WTFJS

    1. Non-coercible objects ToPrimitive ( input [ , Preferre...

  • 2018-01-11

    If(xx) ==的判断 解密 toNumber toPrimitive 对于 Object 类型,先尝试调用 ....

  • 2019-05-28

    今天看到一篇好文——类型转换(ToPrimitive(),ToNumber(),ToString())、加法和连接...

  • js中关于if() 和 == 的判断

    if的判断 原理 举例 判断下面的输出内容 ==的判断 原理 toNumber toPrimitive对于 Obj...

  • 如何实现 a == 1 && a == 2同时成立?

    方法1:valueOf()或者toString()原理:利用 宽松相等的隐式类型转换、ToPrimitive抽象操...

  • ecmasrcipt类型转换

    之前写了一篇 《js中==和===的区别.md》,其中提到了 ToPrimitive(),转换为原始类型的方法。这...

  • js对象的toString()方法和valueOf()方法

    在研究js的==和===的区别时,曾经说过,在js中非原始值对象,要参加运算需要ToPrimitive(x)转换成...

网友评论

    本文标题:ToPrimitive == === > <

    本文链接:https://www.haomeiwen.com/subject/zyvtmctx.html