大家好,我是小梅,公众号:「小梅的前端之路」 原创作者。
作为在前端领域不断探索的一员,在此记录开发中遇到的问题,如果你也遇到过相同的疑惑,希望本文对你有帮助。
写文背景:最近在看《你不知道的JavaScript(中卷)》,其中第一部分的第4章谈到了强制类型转换。作者写得很好,只是这知识它不入脑子啊,我看完就忘了作者讲了些啥,讲了太多的toNumber(),toString(),我全没记住!读完之后,脑袋里只剩下大大的疑惑,连x < y这种比较大小的都不会比较了。为了解决这个迷惑,打开了我下载回来还没怎么看过的ECMA-262.pdf,自我暗示,对着规范看,我肯定能理解了吧。
前提知识:toPrimitive()函数的作用,大家理解成它就是个把Object类型的数据转换成基本数据类型的函数就成,传入基本类型的数据就返回传入的参数本身。此处粘贴规范文档对它的解释。更多具体的转换步骤可以直接去看文档。不同版本的规范文档可能有一些些区别。
1、老生常谈的==和===
首先是非严格相等==,感兴趣的可以查看文档7.2.12 Abstract Equality Comparison章节。
非严格相等的x == y的比较如下:
1、x和y的类型相同,则参照严格相等来进行比较,见下文 2、如果x是null,y是undefined,则返回true; 如果x是undefined,y是null,则返回true 3、如果x、y双方有一个是Number类型,另外一个是String类型,则把String类型转换为Number类型再进行比较(不能转化为数字的将会转化为NaN,则会返回false) 4、如果x、y任意一个是Boolean类型,则将Boolean类型转换为Number类型再进行比较 5、如果x是Number、String、Symbol中的一种类型,而y是Object类型,则,调用toPrimitive(y)将y转换为基本数据类型再进行比较;反之同理。 其他情况,返回false
示例:
const a = {
a1: 1
}
const b = a
const c = {
a1: 1
}
console.log(null == null) // true
console.log(null == undefined) // true
console.log(12 == '12') // true
console.log('12a' == 12 ) // false
console.log(true == true) // true
console.log(a == b) // true, a,b变量所存的对象地址是一样的,因此是true
console.log(a == c) // false
严格相等的x === y的比较如下:
1、x和y的类型不相同,返回false 2、如果x、y都是Undefined或者Null类型的值,则返回true 3、如果x、y都是Number类型,按照Number类型比较,特殊的是x,y都是NaN,返回false;x,y一个为+0,一个为-0返回true, 4、如果是x、y是String类型的,则必须每个字母都相同才返回true,否则返回false 5、如果x、y都是true或者false则返回true 6、如果x、y都是Symbol、Object类型的值,如果他们的值一样(指引用的地址是同一个)则返回true, 否则返回false。
示例:
const a = {
a1: 1
}
const b = a
const c = {
a1: 1
}
console.log(12 === '12') // false
console.log(undefined === undefined) // true
console.log(+0 === -0) // true
console.log(NaN === NaN) // false
console.log('hello' === 'hello') // true
console.log(a === b) // true, a,b变量所存的对象地址是一样的,因此是true
console.log(a === c) // false
2、令人迷惑的x < y
感兴趣的可以查看文档7.2.11 Abstract Relational Comparison章节
x < y的比较步骤如下:
按照规范,首先调用toPrimitive(x, hint Number)如果x和y已经是基本类型,调用toPrimitive将直接返回本身 把x和y都转换成基本数据类型得到px和py,接下来就是比较px和py 1、如果px和py都是String类型的,则按照字符串的比较规则,如果px是py的前缀则返回false,如果字符串px,和py第k位的字母不相同,则比较第k位的两个字母的编码值,如果px='a222',py='b',字母a的编码小于字母b的编码值,因此px < py为true 2、如果px和py都是Number类型的,则正常的数字,相信大家都晓得咋比较 3、如果px是NaN,返回false; 如果py是NaN,返回false;(EMCAScript规范上写的是返回undefined,我在nodejs和浏览器环境中试了,都是返回false) 4、如果px是Infinity, 返回false;如果py是Infinity, 返回true 5、如果px是-Infinity, 返回true;如果py是-Infinity, 返回false 6、如果px是-0 and py是+0,返回false 7、如果px是+0 and py是-0,返回false
示例:
console.log('ac' < 'b') // true
console.log(1 < 2) // true
console.log(NaN < 2, NaN < NaN) // false false
console.log(-Infinity < 2, Infinity < 2) // true false
console.log(12 < -Infinity, 12 < Infinity) // false true
console.log(-0 < +0, +0 < -0 ) // false false
console.log(12 < [123], 124 < [123]) // true false 数据转成字符串'123',再转成Number类型的数字123
console.log(12 < [], -1 < [], -1 < '') // false true true 空数组转成字符串'',空字符串转成数字0
console.log(12 < [123, 'we']) // false 数组转成字符串'123,we',该字符串转Number类型得到的结果是NaN
console.log('[a' < { a: 1 }, {}.toString()) // true [object Object] 对象转成字符串'[object Object]'
3、大家都会的 x + y
感兴趣的可以查看文档12.7.3 The Addition operator ( + )章节。
x + y的比较步骤如下:
首先调用toPrimitive()进行类型转换得到px, py 1、如果px、py有一个为String类型,则将另外一个转换为String类型,结果为拼接后的字符串 2、否则,就把两个都转为Number类型,直接进行算术上的加法。 3、不能转换成Number类型的,直接抛错,如:12 + Symbol(12)将会报错 在调用toPrimitive()的时候没有指定想要转换的类型,默认都是Number,只有日期类型的默认是String
示例:
console.log(12 + '13', typeof (12 + '13')) // 1213 string
console.log(12 + true, typeof (12 + true)) // 13 number ,true转换成Number类型的值是1, false是0
console.log(12 + null, typeof (12 + null)) // 12 number ,null转换成Number类型的值是1
console.log(12 + [], 13 + ['qww', '13'], typeof (12 + [])) // 12 13qww,13 string ,[]转换成String类型的值是''
console.log(12 + {}, typeof (12 + {})) // 12[object Object] string ,对象转换成String类型的值是'[object Object]'
console.log(12 + {a : 1}, typeof (12 + {})) // 12[object Object] string ,对象转换成String类型的值是'[object Object]'
console.log({} + 12) // [object Object]12 ,值得注意的是,当直接再控制台运行{} + 12返回的结果会是12,因为此时{}被看成是代码块而不是对象{}
更多有关toNumber、toString的细节都可以去文档中找答案,此处不再举例。
❤️欢迎素质三连[点赞 + 收藏 + 评论]
我是小梅,有兴趣的话可以在微信搜一搜「小梅的前端之路」第一时间接收文章更新通知,一起沟通、学习成长呀。