JavaScript高阶教程之“==”隐藏下的类型转换


Posted in Javascript onApril 11, 2019

抛砖引玉

按照正常的逻辑来说,我们判断两个值是否相等会遵循以下规则:

JavaScript高阶教程之“==”隐藏下的类型转换

但是我看下面一组值:

[]==0 //true
[]==false //true
[]==!{} //true
[10]==10 //true
'0'==false //true
''==0 //true
undefined==null //true 
!null==true //true

居然没有按照我们的剧本走,那它比较规则又是什么?下面我就来分析一波。

“==”的比较规则

首先我们先去ECMAScript5.1中文版( http://lzw.me/pages/ecmascrip... )找一下“==”的比较规则,如下:

1.若Type(x)与Type(y)相同, 则
    a.若Type(x)为Undefined, 返回true。
    b.若Type(x)为Null, 返回true。
    c.若Type(x)为Number, 则
        i.若x为NaN, 返回false。
        ii.若y为NaN, 返回false。
        iii.若x与y为相等数值, 返回true。
        iv.若x 为 +0 且 y为−0, 返回true。
        v.若x 为 −0 且 y为+0, 返回true。
        vi返回false。
    d.若Type(x)为String, 则当x和y为完全相同的字符序列(长度相等且相同字符在相同位置)时返回true。 否则, 返回false。
    e.若Type(x)为Boolean, 当x和y为同为true或者同为false时返回true。 否则, 返回false。
    f.当x和y为引用同一对象时返回true。否则,返回false。
2.若x为null且y为undefined, 返回true。
3.若x为undefined且y为null, 返回true。
4.若Type(x) 为 Number 且 Type(y)为String, 返回comparison x == ToNumber(y)的结果。
5.若Type(x) 为 String 且 Type(y)为Number,返回比较ToNumber(x) == y的结果。
6.若Type(x)为Boolean, 返回比较ToNumber(x) == y的结果。
7.若Type(y)为Boolean, 返回比较x == ToNumber(y)的结果。
8.若Type(x)为String或Number,且Type(y)为Object,返回比较x == ToPrimitive(y)的结果。
9.若Type(x)为Object且Type(y)为String或Number, 返回比较ToPrimitive(x) == y的结果。
10.返回 false

看完ECMAScript5.1中文版的介绍之后,相信很多小伙伴的心情应该是这样的:

JavaScript高阶教程之“==”隐藏下的类型转换

别看上面说的有点花里胡哨的,其实我们可以用很简单的话来总结出来。由于本篇文章核心是“==”是如何进行类型转换,我就总结一下类型不同的情况下“==”是如何比较的。

  • 当数据类型为Boolean类型或者String类型时,比较时需要转换成Number类型。
  • 当数据类型为引用类型时,需要转换成原始数据类型。当转换后的原始数据类型为Boolean类型或者String类型,则继续转换成Number类型。
  • undefined和null跟任何值在“==”下都返回false,但二者在“==”下返回true。

具体流程图如下:

JavaScript高阶教程之“==”隐藏下的类型转换

备注:

Javascript的数据类型可以分为以下两种:

  • 原始数据类型(null、undefined、Number、String、Boolean、Symbol(Es6才引入的))
  • 引用类型 (Object)

Boolean类型、String类型转换成Number类型的规则(ToNumber)

Boolean类型

Boolean Number
true 1
false 0

 

String类型

标准的数字格式
如果是标准的数字格式,转换成Number类型相比不用多说,比如下面这几个栗子?:

"123" => 123
"12.34" => 12.34
"0.12" => 0.12
"-12.34" => -12.34

非标准的数字格式

但是如果是非标准的数据格式,要分两种情况来考虑:

  • 第一种:只包含数字并且最多只有1个点。
  • 第二种:包含非数字以及含有多个1个点。

只包含数字并且最多只有1个点

这种情况下会首先进行补0和去0的操作,下面看几个栗子?:

"01234" => 1234
".1" => 0.1
"12." => 12
"1.000" => 1
"-02.30" => -2.3

包含非数字以及含有多个1个点

这种情况下统统返回NaN,意为“Not a Number”,这里我们要注意一下,NaN还是Number类型,下面看几个栗子?:

"123aa4" => NaN
"哈喽,DD" => NaN
typeof NaN => "numer"

引用类型转换成原始数据类型的规则(ToPrimitive)

在介绍转换规则之前,首先我们我们介绍一下引用类型都带有的两个方法:

  • Object.prototype.toString
  • Object.prototype.valueOf

这二者都可以将引用类型转换成原始数据类型,接下来我们对二者做一个详细的介绍:

Object.prototype.toString

MDN是这样解释的:

The toString() method returns a string representing the object.(toString()这个方法返回一个代表这个对象的字符串)

举个栗子?:

const obj = {}
console.log(String(obj)) //"[object Object]"
obj.toString = function(){
 return 'Hello,Teacher Cang!'
}
console.log(String(obj)) //"Hello,Teacher Cang!"

Object.prototype.valueOf

MDN是这样解释的:

The valueOf() method returns the primitive value of the specified object.( valueOf()这个方法返回这个对象的原始数据值)

举个栗子?:

const obj = {}
console.log(Number(obj)) //NaN
obj.valueOf = function(){
 return 12345;
}
console.log(Number(obj)) //12345

valueOf和toString的优先级

关于这二者的优先级,在不同的情况下有着不同的优先级,下面我们根据不同情况介绍一下。

ToNumber

看个栗子?:

const obj = {
 toString(){
 console.log('调用了toString');
 return 'Hello,Teacher Cang!';
 },
 valueOf(){
 console.log('调用了valueOf');
 return 12345;
 }
}
console.log(Number(obj)); 

控制台输出结果:
>>>>>>>>>>>>>>>>>>
调用了valueOf
12345

通过上面的代码的运行结果,我们得出这么一个结论:

在ToNumber情况下,valueOf的优先级大于toString。

接下里我们看这么一种情况,如果valueOf返回的并不是原始数据类型会怎么样。

const obj = {
 toString(){
 console.log('调用了toString');
 return 12345;
 },
 valueOf(){
 console.log('调用了valueOf');
 return {};
 }
}
console.log(Number(obj)); 

控制台输出结果:
>>>>>>>>>>>>>>>>>>
调用了valueOf
调用了toString
12345

从上面的运行结果中,我们可以得出这么一个结论:

在ToNumber情况下,如果valueOf返回的不是原始数据类型,则会自动调用toString。

打破砂锅问到底,再来一个非常极端的情况,如果说valueOf和toString返回的都不是原始数据类型,这时又该怎么样呢?同样,我们继续看一下运行结果:

const obj = {
 toString(){
  console.log('调用了toString');
  return {};
 },
 valueOf(){
  console.log('调用了valueOf');
  return {};
 }
}
console.log(Number(obj)); 

控制台输出结果:
>>>>>>>>>>>>>>>>>>
调用了valueOf
调用了toString
Uncaught TypeError: Cannot convert object to primitive value

从上面的运行结果中,我们再次可以得出这么一个结论:

在ToNumber情况下,如果valueOf和toString返回的都不是原始数据类型,那么js会抛出异常,提示无法将引用类型转换原始数据类型。

根据三组代码的运行的结果,我们最后总结一下:

在ToNumber情况下,js会优先调用valueOf,如果valueOf返回的不是原始数据类型,则会接着调用toString,如果toString返回的也不是原始数据类型,js会抛出一个异常,提示无法将引用类型转换原始数据类型。

具体流程图如下:

JavaScript高阶教程之“==”隐藏下的类型转换

ToString

看个栗子?:

const obj = {
 toString(){
  console.log('调用了toString');
  return 'Hello,Teacher Cang!';
 },
 valueOf(){
  console.log('调用了valueOf');
  return 12345;
 }
}
console.log(String(obj)); 

控制台输出结果:
>>>>>>>>>>>>>>>>>>
调用了toString
Hello,Teacher Cang!

通过上面的代码的运行结果,我们得出这么一个结论:

在ToString情况下,toString的优先级大于valueOf。

同样我们看一下,toString返回的值不是原始数据类型时会怎样:

const obj = {
 toString(){
  console.log('调用了toString');
  return {};
 },
 valueOf(){
  console.log('调用了valueOf');
  return 'Hello,Teacher Cang!';
 }
}
console.log(String(obj)); 

控制台输出结果:
>>>>>>>>>>>>>>>>>>
调用了toString
调用了valueOf
Hello,Teacher Cang!

根据运行结果我们可以得出:

在ToString情况下,如果toString返回的不是原始数据类型,则会自动调用valueOf。

最后我们看一下极端情况,二者返回的都不是原始数据类型:

const obj = {
 toString(){
  console.log('调用了toString');
  return {};
 },
 valueOf(){
  console.log('调用了valueOf');
  return {};
 }
}
console.log(String(obj)); 

控制台输出结果:
>>>>>>>>>>>>>>>>>>
调用了toString
调用了valueOf
Uncaught TypeError: Cannot convert object to primitive value

我们又发现:

在ToString情况下,如果toString和valueOf返回的都不是原始数据类型,那么js会抛出异常,提示无法将引用类型转换原始数据类型。

我们将三个结论综合一下:

在ToString情况下,js会优先调用toString,如果toString返回的不是原始数据类型,则会接着调用valueOf,如果valueOf返回的也不是原始数据类型,js会抛出一个异常,提示无法将引用类型转换原始数据类型。

具体流程图如下:

JavaScript高阶教程之“==”隐藏下的类型转换

“==”下的valueOf和toString的优先级

从文章前面我们总结出来“==”的比较流程来看,引用类型转换成原始数据类型之后,如果是Sting类型的话,还要再次转成Number类型,因此“==”下的引用类型转换原始数据类型过程中,遵循ToNumber的优先级规则。

const obj = {
 toString(){
  console.log('调用了toString');
  return 'Hello,Teacher Cang!';
 },
 valueOf(){
  console.log('调用了valueOf');
  return 12345;
 }
}
console.log(obj==12345); 

控制台输出结果:
>>>>>>>>>>>>>>>>>>
调用了valueOf
true
const obj = {
 toString(){
  console.log('调用了toString');
  return 12345;
 },
 valueOf(){
  console.log('调用了valueOf');
  return {};
 }
}
console.log(obj==12345); 

控制台输出结果:
>>>>>>>>>>>>>>>>>>
调用了valueOf
调用了toString
true
const obj = {
 toString(){
  console.log('调用了toString');
  return {};
 },
 valueOf(){
  console.log('调用了valueOf');
  return {};
 }
}
console.log(obj==12345); 

控制台输出结果:
>>>>>>>>>>>>>>>>>>
调用了toString
调用了valueOf
Uncaught TypeError: Cannot convert object to primitive value

“==”之外的类型转换

“==”号只涉及到了ToPrimitive和ToNumber这两种转换,ToBoolean和ToString这两个没有涉及到的我们也介绍一下。

ToBoolean

对于ToBoolean,我们只需要记住几个特例是转成false的,其余的皆为true。

Boolean('') => false
Boolean(undefined) => false
Boolean(null) => false
Boolean(0) => false

ToString

Number to String

Number转String之前,首先会做一个去0和补0的操作,然后再去转成String类型。

String(1.234) => "1.234"
String(NaN) => "NaN"
String(.1234) => "0.1234"
String(1.23400) => "1.234"

Boolean to String

String(true) => "true"
String(false) => "false"

null和undefined to String

String(null) => "null"
String(undefined) => "undefined"

引用类型 to String

引用类型先ToPrimitive转换成原始数据类型,若转换后的原始数据类型不是String类型,再做String类型的转换。

const obj = {
 toString(){
  console.log('调用了toString');
  return true;
 }
}
console.log(String(obj))

控制台输出结果:
>>>>>>>>>>>>>>>>>>
调用了toString
"true"

总结

“==”下的类型转换,要分为两种情况来考虑。第一种,原始数据类型;第二种,引用类型。原始数据类型中String类型和Boolean类型是需要转换成Number类型再去比较的,而引用类型则需要先转换成原始数据类型再进行后续的转换。搞清整个流程之后,我们再去理解“==”涉及到的ToNumber和ToPrimitive是如何进行转换的。按照这样子的理解步骤走,我们对“==”隐藏下的类型转换会有比较清晰的认识。另外,“==”不涉及到ToString和ToBoolean的类型转换,在文章的后面部分我也加上去了,希望可以给小伙伴们一个更加全面的类型转换的认识。最后附上完整的“==”类型转换的流程图:

JavaScript高阶教程之“==”隐藏下的类型转换

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
javascript 写类方式之二
Jul 05 Javascript
javascript之学会吝啬 精简代码
Apr 25 Javascript
javascript拓展DOM操作 prependChild insertAfert
Nov 17 Javascript
JavaScript/jQuery 表单美化插件小结
Feb 14 Javascript
基于javascript实现页面加载loading效果
Sep 15 Javascript
jquery获取复选框的值的简单实例
May 26 Javascript
jQuery中ajax错误调试分析
Dec 01 Javascript
JSON中key动态设置及JSON.parse和JSON.stringify()的区别
Dec 29 Javascript
js下拉菜单生成器dropMenu使用方法详解
Aug 01 Javascript
从零开始在NPM上发布一个Vue组件的方法步骤
Dec 20 Javascript
js实现贪吃蛇小游戏(加墙)
Jul 31 Javascript
原生小程序封装跑马灯效果
Oct 21 Javascript
使用Vue父子组件通信实现todolist的功能示例代码
Apr 11 #Javascript
详解jQuery设置内容和属性
Apr 11 #jQuery
js作用域和作用域链及预解析
Apr 11 #Javascript
关于js陀螺仪的理解分析
Apr 11 #Javascript
angular 表单验证器验证的同时限制输入的实现
Apr 11 #Javascript
angular 实现同步验证器跨字段验证的方法
Apr 11 #Javascript
vue实现新闻展示页的步骤详解
Apr 11 #Javascript
You might like
关于php循环跳出的问题
2013/07/01 PHP
php格式化日期实例分析
2014/11/12 PHP
PHP实现八皇后算法
2019/05/06 PHP
巧妙破除网页右键禁用的十大绝招
2006/08/12 Javascript
【消息提示组件】,兼容IE6/7&&FF2
2007/09/04 Javascript
js自定义方法通过隐藏iframe实现文件下载
2013/02/21 Javascript
JS中的THIS和WINDOW.EVENT.SRCELEMENT详解
2015/05/25 Javascript
jQuery右侧选项卡焦点图片轮播特效代码分享
2015/09/05 Javascript
jQuery中slidedown与slideup方法用法示例
2016/09/16 Javascript
Javascript 跨域知识详细介绍
2016/10/30 Javascript
AngularJS使用ng-inlude指令加载页面失败的原因与解决方法
2017/01/19 Javascript
微信小程序 同步请求授权的详解
2017/08/04 Javascript
webpack处理 css\less\sass 样式的方法
2017/08/21 Javascript
Node.js创建Web、TCP服务器
2017/12/05 Javascript
H5 js点击按钮复制文本到粘贴板
2020/11/19 Javascript
基于jQuery拖拽事件的封装
2020/11/29 jQuery
vue+element table表格实现动态列筛选的示例代码
2021/01/14 Vue.js
Python端口扫描简单程序
2016/11/10 Python
Tensorflow中使用tfrecord方式读取数据的方法
2018/06/19 Python
Python输出\u编码将其转换成中文的实例
2018/12/15 Python
浅谈python编译pyc工程--导包问题解决
2019/03/20 Python
关于初始种子自动选取的区域生长实例(python+opencv)
2020/01/16 Python
如何对python的字典进行排序
2020/06/19 Python
Python语言编写智力问答小游戏功能
2020/10/13 Python
基于CSS3的animation属性实现微信拍一拍动画效果
2020/06/22 HTML / CSS
HTML5实现表单自动验证功能实例代码
2017/01/11 HTML / CSS
ALDO加拿大官网:加拿大女鞋品牌
2018/12/22 全球购物
三八妇女节活动主持词
2014/03/17 职场文书
班训口号大全
2014/06/18 职场文书
学校交通安全责任书
2014/08/25 职场文书
中学生打架检讨书
2014/10/13 职场文书
志愿者事迹材料
2014/12/26 职场文书
2015年医德医风工作总结
2015/04/02 职场文书
服装店员工管理制度
2015/08/07 职场文书
Nginx 过滤静态资源文件的访问日志的实现
2021/03/31 Servers
Python insert() / append() 用法 Leetcode实战演示
2021/03/31 Python