本文介绍几个Javascript里经常遇到的一些坑。
一、console.log打印对象,折叠属性延时快照
几乎所有的前端都要被这个问题坑一次,而且一旦踩到这坑到你爬出来,往往不下半小时。如果你还不知道这个问题,相信我,你将在某次调试中不经意间遇到它,所以你可以花三分钟了解一下。
console.log是前端最常用的调试办法之一,其功能是打印一条消息到浏览器控制台。console.log的坑在于,其打印的变量内容,并非打印时的快照,这在我们查看打印值的时候会给我们带来巨大困扰。具体表现为:如果我们打印一个对象之后修改了对象属性值,这时候我们再点开对象的折叠属性,控制台显示该属性值的是修改后的值而非打印时刻的值!!!
示例:
let a = { b: { c: 1 } }
console.log(a)
a.b.c = 2
打印结果默认是一个属性折叠的对象:
我们展开打印结果:
打印a时,a.b.c的值是1,然而当我们把a展开却发现a.b.c的值变成了2!
实际上,console.log打印的对象属性值,取决于该对象第一次被展开是的值,而非打印时的值。
二、时间对象的方法getMonth()和getDay()
写一个简单的日期格式化函数,该函数接收一个时间戳和一个日期格式,返回按照指定格式格式化后的日期字符串:
function formatTime (time, format = 'yyyy-MM-dd') {
const formatNum = num => String(num).padStart(2, '0')
const date = new Date(time)
return format.replace(/yyyy/g, date.getFullYear())
.replace(/MM/g, formatNum(date.getMonth()))
.replace(/dd/g, formatNum(date.getDate()))
}
我们本想将yyyy、MM、dd分别替换为年、月、日,运行代码后发现,年、日正常转换了,而月份却出现了异常。今天是2020年3月24日,调用上述函数:
打印的时间居然是“2020年02月24日”,月份少了1。原来,getMonth返回基于0的值,即0 代表一月份,1 代表二月份, 2 代表三月份,依此类推。作为程序员而言,一个序列从0开始应该还是可以接受(毕竟许多语言的数组下标都是从0开始的)。然而在日期这里,getDate返回1~31的数字,表示一个月中的哪一日,而getMonth却返回0~11中的一个数字,这二者的不一致性确实不太科学。
另外一个有着相似坑的函数是getDay,它可以获取当前日期是本周的周几,其返回值也是从0开始的,不过0代表周日,1代表周一,依此类推:
三、sort默认排序
sort是JS里用来排序的函数,默认是升序排序,不过这里的“升序”与我们的认知略有不同。这个坑最常见于数字排序,先来一个简单的排序:
数组升序排序了,很方便吧,再来一个:
很明显并不是我们所期望的升序。MDN对于sort的描述:如果没有指明 compareFunction
,那么元素会按照转换为的字符串的诸个字符的Unicode位点进行排序。所以在对字符串排序时,会先将元素转为字符串,再排序。因为字符‘1’出现在‘3’, ‘5’, ‘7’之前,所以13被排在了这三个数字以前。
所以通常使用sort的时候,我们要注意给sort传递一个比较函数:
四、比较不能传递
假设又一个值value,我们想在它在某个区间[min, max]之间时执行某个逻辑,我们有可能在不经意间写出下面这样的代码:
if (min <= value <= max) {
// xxx
}
然而不幸的是,JS中上述代码是不能正常工作的,因为JS的比较不能传递。
为什么4 > 3 > 2是false呢?因为4 > 3 > 2相当于(4 > 3) > 2,即true > 2,这里会进行类型转换,true转为1,所以结果就是false。