关于 == 操作符里的隐式转换2018-08-18

前言

类型转换在各个语言中都存在,而在 JavaScript 中由于缺乏对其的了解而不慎在使用中经常造成bug被人诟病。为了避免某些场景下的意外,甚至推崇直接使用 Strict Equality( === )来代替 ==(最新的eslint规则默认就是使用===)。这确实能避免很多bug,但更是一种对语言不理解的逃避(个人观点)。

来看几道经典的题目:

  [] == [] // false
  [] == ![] // true
  {} == !{} // false
  {} == {} // false
是不是很奇怪?本文将从书中看到的知识与规范相结合,来详细说明一下JavaScript在类型转换时候发生的故事。

JS的数据类型

首先,回想一下JS的类型都有什么。
原始值(primitives): undefined, null, booleans, numbers,strings, symbol(es6)
对象值(objects): Object

ToPrimitive

在发生转换的时候,js其实都是会将操作对象转化为原始的对象,这也是最为诟病的地方,因为js很难直接抛出错误,她会用一套自己的方法去理解我们的错误,并做相应的调整,哪怕这些错误我们是无意识的。所以我们要知道她的转换方式,才能做到知己知彼,对代码的控制更为精准。

签名:ToPrimitive(input, PreferredType?) //PreferredType: Number 或者 String
流程如下:

input为原始值,直接返回;
不是原始值,调用该对象的valueOf()方法,如果结果是原始值,返回原始值;
调用valueOf()不是原始值,调用此对象的toString()方法,如果结果为原始值,返回原始值;
如果返回的不是原始值,抛出异常TypeError。
其中PreferredType控制线调取valueOf()还是toString()。

ps: Date类型按照String去调用。

总结:==操作符

比较运算 x==y,其中x和y是值,产生true或者false。这样的比较如下方式进行:

一、若Type(x)与Type(y)相同,则:
   a.若Type(x)为Undefined,返回true。
   b.若Type(x)为Null,返回true。
   c.若Type(x)为Number,则:
    1.若x为NaN,返回false。
    2.若y为NaN,返回false。
    3.若x与y为相等数值,返回true。
    4.若x为+0且y为-0,返回true。
    5.若x为-0且y为+0,返回true。
    6.返回false。
   d.若Type(x)为String,则当x和y完全相等的字符序列(长度相等且相同字符在相同位置)时返回true。否则,返回false。
   e.当Type(x)为Boolean,当x和y同为true或者同为false时返回true。否则,返回false。
   f.当x和y为引用同一对象时返回true。否则,返回false。
二、若x为null且y为undefined,返回true。
三、若x为undefined且y为null,返回true。
四、若Type(x)为number且Type(y)为String,返回comparison x==ToNumber(y)的结果。
五、若Type(x)为String且Type(y)为Number,返回comparison ToNumber(x) == y的结果。
六、若Type(x)为Boolean,返回比较ToNumber(x) == y的结果。
七、若Type(x)为Boolean,返回比较x == ToNumber(y)的结果。
八、若Type(x)为String或Number,且Type(y)为Object,返回比较x == ToPrimitive(y)的结果。
九、若Type(x)为Object且Type(y)为String或Number,返回比较ToPrimitive(x) == y的结果。
十、返回false。

上图中的 toPrimitive 就是对象转基本类型。
这里来解析一道题目 [] == ![] // -> true ,下面是这个表达式为何为 true 的步骤
// [] 转成 true,然后取反变成 false
[] == false
// 根据第 8 条得出
[] == ToNumber(false)
[] == 0
// 根据第 10 条得出
ToPrimitive([]) == 0
// [].toString() -> ''
'' == 0
// 根据第 6 条得出
0 == 0 // -> true

备注

ToString

按照以下规则转化被传递的参数

ArgumentType Result
Undefined “undefined”
Null “null”
Boolean true -> “true”false – > “false”
Number 1:’NaN -> “NaN”。 2:+0 -0 -> “0”。 3:-1 -> “-1。 4:”infinity -> “Infinity”
String 不转换 直接返回
Object 1:调用ToPrimitive抽象操作, hint 为 String 将返回值作为 value。2:返回ToString(value)。
String(undefined) // "undefined"
String(null) // "null"
String(true) // "true"
ToNumber

按照以下规则转换被传递参数

Argument Type Result
Undefined NaN
Null +0
Boolean 1:true -> 1。 2:false -> +0。
Number 直接返回
String 如果不是一个字符串型数字,则返回NaN(具体规则见规范9.3.1)
Object 1:调用ToPrimitive抽象操作, hint 为 Number 将返回值作为 value。 2:返回ToNumber(value)。
ToBoolean

按照以下规则转换被传递参数

Argument Type Result
Undefined false
Null false
Boolean 直接返回
Number 1:+0 -0 NaN -> false。 2:其他为true。
String 1:空字符串(length为0) -> false。 2:其他为true。
Object true
ToPrimitive

顾名思义,该抽象操作定义了该如何将值转为基础类型(非对象),接受2个参数,第一个必填的要转换的值,第二个为可选的hint,暗示被转换的类型。

按照以下规则转换被传递参数

Argument Type Result
Undefined 直接返回
Null 直接返回
Boolean 直接返回
Number 直接返回
String 直接返回
Object 返回一个对象的默认值。一个对象的默认值是通过调用该对象的内部方法[[DefaultValue]]来获取的,同时传递可选参数hint。
[[DefaultValue]] (hint)

当传递的hint为 String 时候,

  • 如果该对象的toString方法可用则调用toString
      如果toString返回了一个原始值(除了object的基础类型)val,则返回val
        如果该对象的valueOf方法可用则调用valueOf方法
      如果valueOf返回了一个原始值(除了object的基础类型)val,则返回val
        抛出TypeError的异常
      当传递的hint为 Number 时候,
  • 如果该对象的valueOf方法可用则调用valueOf方法
      如果valueOf返回了一个原始值(除了object的基础类型)val,则返回val
        如果该对象的toString方法可用则调用toString
      如果toString返回了一个原始值(除了object的基础类型)val,则返回val
        抛出TypeError的异常
  • hint的默认值为Number,除了Date object
    举个栗子
var a = {}
a.toString = function () {return 1}
a.valueOf = function () {return 2}
String(a) // "1"
Number(a) // 2
a + '' // "2"   ???????
+a // 2
a.toString = null
String(a) // "2"
a.valueOf = null
String(a) // Uncaught TypeError: balabala

似乎我们发现了一个很不合规范的返回值,为什么 a + ''不应该返回”1″吗

问题的答案其实很简单 + 操作符会对两遍的值进行 toPrimitive 操作。由于没有传递 hint 参数,那么就会先调用a.valueOf 得到2后因为+右边是字符串,所以再对2进行ToString抽象操作后与””的字符串拼接。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343