JS时间戳的一些记录

时间戳的概念和历史有兴趣可以看看

http://jerey.cn/time/2018/02/10/Time%E7%B3%BB%E5%88%97%E4%B9%8B%E5%90%84%E4%B8%AA%E6%97%B6%E9%97%B4%E7%9A%84%E5%90%AB%E4%B9%89/

浏览器中JS获取时间戳我知道的有以下6种方法,这里主要记录以下他们的使用场景不同和性能优劣(忽略不计,但是你需要知道,不然怎么应对面试官)以及为什么。

为什么说浏览器中,因为Nodejs甚至别的JS引擎可能还有别的方法。

Date.parse(str)

Date.parse() 方法解析一个表示某个日期的字符串,并返回从1970-1-1 00:00:00 UTC 到该日期对象(该日期对象的UTC时间)的毫秒数,如果该字符串无法识别,或者一些情况下,包含了不合法的日期数值(如:2015-02-31),则返回值为NaN。

很明显我们可以看到这是Date类的静态方法。所以他不需要实例化。

根据MDN文档的说明这是一层显示调用。实际是使用了我们接下来要介绍的一种方法(接着看)。

new Date().getTime()

getTime() 方法返回一个时间的格林威治时间数值。

你可以使用这个方法把一个日期时间赋值给另一个Date 对象。这个方法的功能和 valueOf() 方法一样。

这个就是Date.parse的隐示调用。所以我们把两个结合起来说。

因为getTime是Date.parse的实际实现,所以无需测试就可以判断getTime的性能是优于Date.parse的。至于为什么,因为parse无论如何多一层调用栈,所以设getTime的开销为n,那么paser的开销至少是n+1。(所以我没有去做测试,如果有错请告诉我,更何况在实例化之前还有别的操作增加开销)

那么既然如此为何还要多此一举做一个静态API呢?

这里我们就可以看到使用层面了,先说Date.parse。

因为可以接收任意的时间格式字符串,实际上相当于帮我们做了一步 new Date(str)的封装。所以当你碰到任意一个日期字符串想转换成时间戳的时候都可以可以直接用这个。比如Date.parse('2021-02-07')。

那么new Date().getTime()呢?这里我们实际拆成两步看,第一是实例化Date对象。然后是获取时间戳。换句话说这个实例你可以是现new的,也可以是其他任何方式早就准备好的。所以我们在想获取一个Date对象的时间戳的时候就可以用这个。当然你非要说两者差不多的话,我可以告诉你,他就是一个东西,两种写法,方便调用而已。嗯,我目前的理解就是这样。

然后这里插一个小知识。

console.log(Date.parse('2021/02/07'))
// 1612627200000
console.log(Date.parse('2021-02-07'))
// 1612656000000

可以看到不同的分隔符其实是差了8小时的(当然这只是因为我在中国吧)。所以就可以看到因为分隔符的不同解析也不同。具体的原因可以看看这个,你不看也可以,我会总结的

https://segmentfault.com/a/1190000003710954

大致就是以'/'为日期分隔符的日期字符串,在parse里会被解析成UTC时间,也就是对我们来说是外国人的2021年2月7日。而'-'分隔的日期字符串会被解析成当地时区时间。也就是我们中国人的2021年2月7日。但是因为我们在东八区,所以实际上是比外国人多过了8小时才到一致的时间的。所以就有了时间戳的差异。另外IOS也不识别'-'分隔,所以如果哪天你的IOS页面上时间出了问题,可以看看是不是这个原因。

然后我们接着来说第三种方式。

Date.now()

这个方法我找了很多地方没有找到底层实现代码,但是不妨碍我们通过调用方式猜想一下其中的机制。

首先根据ES规范他是返回一个Number类型的时间戳,而这个时间戳就是当now方法调用的那个时刻的时间戳。

而now又是Date的静态类方法,获取的时间戳又是单一的所谓现在。所以我们假设有一个系统API会给到JS某个时刻的时间戳的话,当前时刻的获取无疑是最快的。当然了这是猜想,因为我找不到实现的相关说明。但是从性能来说,我的猜想没毛病。稍后我会在文末放出大佬做过的性能评测。结论目前(不考虑不支持now的情况)就是now > getTime > parse

但是这个Date.now性能这么好对我们来说使用场景却受到了限制,也就是我们不能通过now获取自定义时间的时间戳。所以像这个我们一般可以用在调用接口签名等等场景。

好了抬走,下一个。

new Date().valueOf()

这个你也不要问我为什么,文档说返回原始值。所以他就返回了时间戳。所以我的理解就是Date对象的原始值就是时间戳值。这里查了比较的资料显示实际行为和getTime是一样的。但是经过我的实测性能是略差于getTime的。而又可知道valueOf是挂在Object.prototype上的。所以在Date上调用还要往上走一层,而经过性能结果反推,猜测getTime是缓存了valueOf方法等等相似的内部实现。所以寻找距离短,因而性能更好。

+new Date()

这里我先告诉你们结论,就是它的性能并没有比上面的三兄弟好。那为什么?

这先分两步看+在这里实际是一个Number(xxx)的调用过程。然后其中的xxx是一个Date对象实例化的过程。

而Number构造函数在面对Date对象的时候实际上是调用了Date的valueOf方法。所以就有了这个结果。

上面这么多的方法,结果虽然一样,但是他们内部走的步数却都不相同。理解了他们对我们深入理解JS会有很好的帮助。

抬走再下一个。

performance.timeOrigin + performance.now()

performance这个对于浏览器性能有了解一些的都认识。它提供了一系列性能相关的API,但这不是今天的重点。

timeOrigin是表示这个JS环境也就是你的页面的起始时间(创世时间),而performance.now()这个方法返回从创世时间到目前过了多少毫秒。所以我们可以通过这种迂回的方式来获取当前时间戳。

即 当前时间 = 起始时间 + 消耗时间

而在我后面提供的那个大佬的文章里面说这个是性能最好的。本着怀疑的态度,我测试了一下。结果却不是这样。

直接上图

可以看到在足够的数量级下面我的现场测试,performance反而是性能最差的,这个我其实不是很奇怪。我的猜测也是我对他怀疑的原因在于,我看到这个组合方式的时候。

我们分两步看,第一步performance.timeOrigin 从performance对象中读取起始时间,是一个找到对象并找到属性的过程。第二步,performance.now(),根据我们上面的猜测,时间戳的最终获取其实是系统API给的结果,那么反推这个now怎么出来的?那肯定是 now = 当前时间 - 起始时间 。所以这样的猜测才能符合我们这个测试的结果。

你不要问我为什么猜,因为我真的找不到哪里讲了具体实现。而且就算找不到,这种推测的过程才是最刺激的不是吗?结果并不重要!

最后抬走,下一个?没有了我也目前只知道这些,有多的一定要告诉我。

然后是大佬的文章里面有测试说明,虽然和我的不一样,但是他的很详细。

https://www.jianshu.com/p/d601f22f38dc

javascript
54 views
Comments
登录后评论
Sign In