开胃菜
先说一个题外话,我在工作中遇到一个问题,需要比较 "08:00"
和 "09:00"
的大小,最后我找到三种方法:
- 在两个字符串前后各拼接相同的年月日和秒,拼成完整的时间格式进行比较:
var head = "2016-01-01 "
var foot = ":00"
var time1 = head + "08:00" + foot //"2016-01-01 08:00:00"
var time2 = head + "09:00" + foot //"2016-01-01 09:00:00"
剩下的就不说了,比较两个完整的日期还是很容易的。
- 把两个字符串中的冒号去掉,转换成数字进行比较:
function timeToNumber(time) {
let [head,foot] = time.split(":")
return Number(head+foot)
}
var time1 = timeToNumber("08:00") //800
var time2 = timeToNumber("09:00") //900
- 直接比较
对,你没有看错,直接比较两个字符串:
"08:00" > "09:00" //false
看到这里估计有人就纳闷了,很明显第三种方法是更简洁的,但是字符串比较,好像很少见,它比较的依据是什么呢?
其实,字符串比较大小,会从左到右依次取两个字符串中的字符,两两比较他们charCodeAt()
的结果,直到比较出大小就停止。比如:
var str1 = "a11"
var str2 = "a2"
// str1 和 str2 比较的时候,会先比较 str1[0] 和 str2[0],两个都是 "a",比较下一个
// str1[1] 是"1",charCodeAt()是49,str2[1] 是"2",结果是50,所以 str1[1] < str2[1],对比结束
// 最终结果 str1 < str2
同理,在比较"08:00"
和 "09:00"
的时候,先比较两个"0"
,发现一致之后比较"8"
和"9"
,所以"08:00" < "09:00"
。
这里有一个问题就是,时间格式必须保持一致,位数不够的记得补"0"
,拿"8:00"
和"10:00"
比较会发现结果有问题,必须拿"08:00"
和"10:00"
比较才可以。
这个问题就说到这里,大家有其他的方法可以留言补充,给大家提供不同的思路。开胃菜结束,进入正题。
正题
作为一个爱(记)学(不)习(清)的好(笨)孩子,通过字符串比较这件事,我意识到还有更多的非相同类型的比较,比如字符串和数字的比较,布尔和数组的比较(我疯了么我这么用),另外还有加减乘除等其他操作符。
我觉得有必要整理一下了。
我第一反应是这张图:
真是迷人的笑容呢 :)
在比较之前,我们需要先了解下各种数据类型转化的结果有哪些。
转数字
-
字符串:
- 空字符串是0
- 字符串头尾有空格会忽略
- 空格在中间,或者字符串中含有非数字类型字符,转换结果就是
NaN
- 布尔:
true -> 1
,false -> 0
- undefined字:
NaN
- null: 0
-
数组:
- 空数组是0
- 如果数组中有且只有一项是数字元素,转换为数字
- 其他情况
NaN
-
对象:
- 如果对象有
valueOf()
方法,就调用该方法。如果返回基本类型值,就将这个值转化为数字 - 如果对象没有
valueOf()
方法或者该方法返回的不是基本类型值,就会调用该对象的toString()
方法。如果存在且返回值是基本类型值,就转化为数字 - 否则就报错
- 如果对象有
- 函数:NaN
转字符串
-
undefined
->"undefined"
-
null
->"null"
-
true
->"true"
/false
->"false"
- 数字:极小和极大的数字使用指数形式,一般的情况你懂得
-
对象:
- 如果对象有
toString()
方法,就调用toString()
方法。如果该方法返回基本类型值,就将这个值转化为字符串 - 如果对象没有
toString()
方法或者该方法返回的不是基本类型值,就会调用该对象的valueOf()
方法。如果存在且返回值是基本类型值,就转化为字符串 - 否则就报错
- 除非自行定义,否则
toString()
返回内部属性[[Class]]
的值,如"[object Object]"
- 如果对象有
转布尔
- 所有的假值(
undefined
、null
、+0
、-0
、NaN
、""
)会被转化为false
,其他都会被转为true
- 所以,空对象、空数组都是
true
转对象
-
null
和undefined
转对象直接抛异常 - 基本类型通过调用
String()
、Number()
、Boolean()
构造函数,转换为他们各自的包装对象
使用场景
知道了各种数据类型转化的规则,那么在不同的场景中,究竟是怎么使用的呢?
== 运算符
常见的误区是:==
检查值是否相等,===
检查值和类型是否相等。
正确的解释是:==
允许在相等比较中进行强制类型转换,而===
不允许。
事实上,==
和===
都会检查操作数的类型,区别在于类型不同时它们的处理方式不同。
- 如果一个值是
null
,另一个值是undefined
,则相等 - 如果一个是字符串,另一个值是数字,则把字符串转换成数字,进行比较
- 如果任意值是
true
,则把true
转换成1再进行比较;如果任意值是false
,则把false
转换成0再进行比较 -
如果一个是对象,另一个是数值或字符串,把对象转换成基础类型的值再比较
- 对象转基础类型时,优先调用
valueOf()
,再调用toString()
。 - 例外的是
Date
,Date
利用的是toString()
转换
- 对象转基础类型时,优先调用
经典题
[] == false // true
!![] // true
//原因是 == 两边都转为数字进行比较,而不是 [] 转为布尔值与 false 比较
+ 运算符
+ 运算符可以作为一元运算符使用,此时的作用是将后边跟着的数据转为数字
+true // 1
+[] // 0
+new Date() //获取当前时间的Unix时间戳
在作为二元运算符使用时,+
运算符比- * /
运算符要复杂一些,因为其他的运算符都是处理数字的,而+
运算符还可以处理字符串拼接。
- 两边如果有字符串,另一边会转化为字符串进行相加
- 如果没有字符串,两边都会转化为数字进行相加,对象也根据前面的方法转化为数字
- 如果其中的一个操作数是对象,则将对象转换成原始值,日期对象会通过
toString()
方法进行转换,其他对象通过valueOf()
方法进行转换,但是大多数都是不具备可用的valueOf()
方法,所以还是会通过toString()
方法执行转换
简单来说就是,如果+
运算符的其中一个操作数是字符串(或者通过以上步骤可以得到字符串),那么就执行字符串拼接,否则执行数字加法。
经典题
!+[]+[]+![] //"truefalse"
//首先第一个 + 左边不是数值,所以它是一元运算符,将后边跟着的 [] 转化为数字 0
//同时,最后一个 [] 左边是 ! 运算符,将 [] 转化为布尔值并取反,为 false
//转化后的结果为 !0 + [] + false
//!0 结果为 true,[] 转化为 "",所以结果变为 true + "" + false
//因为 第一个 + 右边有字符串,所以变为"true" + false
//最终结果为 "truefalse"
条件判断
以下条件判断的语句,会发生隐式转换为布尔值的情况:
-
if()
语句中的条件判断表达式 -
for(..; ..; ..)
语句中的条件判断表达式 -
while()
和do .. while()
-
? :
中的条件判断表达式 -
||
和&&
左边的操作数
补充:valueOf()和toString()
常用内置对象调用toString()
和valueOf()
的返回情况
类型 | toString | valueOf |
---|---|---|
Object | "[object 类型名]" |
对象本身 |
String | 字符串值 | 字符串值 |
Number | 返回数值的字符串表示。还可返回以指定进制表示的字符串,默认10进制 | 数字值 |
Boolean |
"true" / "false"
|
Boolean 值 |
Array | 每个元素转换为字符串,用英文逗号作为分隔符进行拼接 | 数组本身 |
Date | 日期的文本表示,格式为Wed Jun 05 2019 18:22:32 GMT+0800 (中国标准时间)
|
返回时间戳,等同于调用getTime()
|
Function | 函数的文本表示 | 函数本身 |
RegExp | 正则的文本表示 | 正则本身 |
以上是本篇文章的内容,欢迎大家提出自己的想法,我们一起学习进步,与君共勉。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。