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 AOP 实现ajax回调函数使用比较方便
Nov 20 Javascript
JS Replace 全部替换字符的用法小结
Dec 24 Javascript
js函数在frame中的相互调用详解
Mar 03 Javascript
JavaScript中常见获取元素的方法汇总
Mar 04 Javascript
IE10中flexigrid无法显示数据的解决方法
Jul 26 Javascript
JS实现探测网站链接的方法【测试可用】
Nov 08 Javascript
js图片延迟加载(Lazyload)三种实现方式
Mar 01 Javascript
jQuery+Ajax请求本地数据加载商品列表页并跳转详情页的实现方法
Jul 12 jQuery
DVA框架统一处理所有页面的loading状态
Aug 25 Javascript
vue 修改 data 数据问题并实时显示的方法
Aug 27 Javascript
vue中的watch监听数据变化及watch中各属性的详解
Sep 11 Javascript
vue3 自定义图片放大器效果的示例代码
Jul 23 Vue.js
使用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解析html类库simple_html_dom(详细介绍)
2013/07/05 PHP
CodeIgniter框架过滤HTML危险代码
2014/06/12 PHP
thinkPHP简单遍历数组方法分析
2016/05/16 PHP
JS加ASP二级域名转向的代码
2007/05/17 Javascript
常用的javascript function代码
2008/05/23 Javascript
Document 对象的常用方法
2009/07/31 Javascript
使用js获取QueryString的方法小结
2010/02/28 Javascript
用js小类库获取浏览器的高度和宽度信息
2012/01/15 Javascript
Javascript绝句欣赏 一些经典的js代码
2012/02/22 Javascript
javascript学习笔记(三) String 字符串类型介绍
2012/06/19 Javascript
根据身份证号自动输出相关信息(籍贯,出身日期,性别)
2013/11/15 Javascript
jquery让返回的内容显示在特定div里(代码少而精悍)
2014/06/23 Javascript
Js实现网页键盘控制翻页的方法
2014/10/30 Javascript
整理Javascript函数学习笔记
2015/12/01 Javascript
基于JS实现新闻列表无缝向上滚动实例代码
2016/01/22 Javascript
jQuery查找节点方法完整实例
2016/09/13 Javascript
Javascript 闭包详解及实例代码
2016/11/30 Javascript
微信小程序 Tab页切换更新数据
2017/01/05 Javascript
vue-cli脚手架引入图片的几种方法总结
2018/03/13 Javascript
JS求1到任意数之间的所有质数的方法详解
2019/05/20 Javascript
Python的Flask框架中实现简单的登录功能的教程
2015/04/20 Python
Python实现将xml导入至excel
2015/11/20 Python
如何用Python破解wifi密码过程详解
2019/07/12 Python
Windows下实现将Pascal VOC转化为TFRecords
2020/02/17 Python
python如何提取英语pdf内容并翻译
2020/03/03 Python
利用Opencv实现图片的油画特效实例
2021/02/28 Python
阿里巴巴的Oracle DBA笔试题答案-SQL tuning类
2016/04/03 面试题
审计工作个人的自我评价
2013/12/25 职场文书
医院总经理岗位职责
2014/02/04 职场文书
党员承诺书格式
2014/05/21 职场文书
2014年专项整治工作总结
2014/11/17 职场文书
2014年食品安全工作总结
2014/12/04 职场文书
2015年法制宣传月活动总结
2015/03/26 职场文书
在校大学生才艺比赛策划书怎么写?
2019/08/26 职场文书
vue-cropper插件实现图片截取上传组件封装
2021/05/27 Vue.js
vue项目如何打包之项目打包优化(让打包的js文件变小)
2022/04/30 Vue.js