在一个对象声明中使用了两个Date.now()

19

假设有var o = {a:Date.now(), b:Date.now()}

o.a === o.b是否总是true?(我主要关心Node.JS。)

5个回答

23

不管规范怎么说,Date.now 可以在运行时被用户自定义的函数替换。这在 Node 和浏览器中都适用:

let oldNow = Date.now;
Date.now = function () {
  let wait = oldNow() + 1000;
  while (oldNow() < wait) {
    // wait one second
  }
  return oldNow();
}

有了这个设定,每个调用至少需要一秒钟,因此你的两次调用将永远不相等。

当我们查看Date.now规范 (15.9.4.4)时,它只是简单地说它返回

指定调用 now 时刻的 UTC 日期和时间的时间值。

这并不能保证两个调用会返回相同的值。从我所知道的来看,规范指定Date对象具有毫秒精度 (15.9.1.1),但对准确性没有任何保证。

由于底层计时器不精确并且两个调用在同一个滴答中发生,因此很可能在同一行中进行两个调用将大概率返回相同的时间,但规范似乎没有指定。


这不会阻塞事件循环一秒钟吗? - MinusFour
@MinusFour 是的。这是一个简单的证明,Date.now 可能返回两个不同的值(尽管有一段时间,Omniture 中使用了非常相似的代码,导致了许多问题)。 - ssube
4
有趣的是,你的例子显示Date.now可以在运行时被一个用户定义的函数替换,该函数始终返回相同的值,这种情况下语句将始终为真。当然,我只是开玩笑... - ci_

9
不,试试这个简单的测试吧:

var test = new Array();
for(var i=0; i<40000; i++){
  test.push({a:Date.now(), b:Date.now()});
}
for(var i=0 ; i<test.length; i++){
  if(test[i].a != test[i].b){
     console.warn('a & b !=');
  }
}

And you will see what happen !


5
即使在某些JS引擎实现中这两个可能总是相等的,从逻辑上讲它们仍然是不同的时间点,因此我会将它们视为两个不同的时间点。

3
如果您对精度感兴趣,我建议使用performance.now()。 这里是一个有关API的入门指南 发现HR时间API。这里是一些特定于Node的信息:Node hrtime

2
使用 performance.now() 我可以得到这两个值的差异!谢谢。 - Randomblue
啊,从你的问题中并不清楚是想让它们不同还是确保它们相同。很高兴我能帮到你。 - Michael Blackburn

2

不。

你的问题中隐含了两个问题:

  1. 对象初始化器是否有特殊的功能,可以查看属性初始化值并对其进行“优化”或其他操作以避免冗余调用?

  2. 在快速连续调用两次Date.now时,它们返回的值是否总是相同的?

答案是不会,不会。 :-) 让我们更详细地看一下:

对象初始化器

对象初始化器是逐步处理的。这在规范中有详细说明(从这里开始),虽然在之前的第5版规范中更容易理解,但基本上它说对象会被创建,然后按照源代码顺序一个接一个地添加属性。所以有:

var o = {
    a: "one",
    b: "two"
};

首先JavaScript引擎创建对象,然后评估属性初始化程序的右侧以获取a("one"),添加具有结果值的a属性,然后评估属性初始化程序的右侧以获取b("two"),然后添加具有结果值的b属性。因此我们知道右侧是分别评估的,并且在过程中的不同时刻。这就是为什么使用以下代码:
var value = 0;
function unique() {
    return value++;
}
var o = {
   a: unique(),
   b: unique()
};

var value = 0;
function unique() {
    return value++;
}
var o = {
   a: unique(),
   b: unique()
};
snippet.log("o.a = " + o.a + ", o.b = " + o.b);
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

"...a 可靠地获得 0b 可靠地获得 1。因此,我们知道在这种情况下:"
var o = {
    a: Date.now(),
    b: Date.now()
};

“Date.now”快速连续调用

在快速连续调用“Date.now”时,会进行两次调用,这两次调用可能会返回不同的值。这是一个基于时间的函数,如果你在毫秒级别的时间变化前后进行调用,就会得到不同的值。虽然对于计算机来说,一毫秒是很长的时间,但也不是非常长。

我们可以通过实验证明它确实会发生:

var first, second;
var counter = 0;
snippet.log("Start");
do {
  first = Date.now();
  second = Date.now();
  ++counter;
} while (first === second);
snippet.log("Stop, values were different: " + first + " !== " + second + "; counter = " + counter);

var first, second;
var counter = 0;
snippet.log("Start");
do {
    first = Date.now();
    second = Date.now();
    ++counter;
} while (first === second);
snippet.log("Stop, values were different");
snippet.log(first + " !== " + second);
snippet.log("counter = " + counter);
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

上述代码总是停止,证明快速连续调用会返回不同的值。对于我来说,在Linux上的Chrome浏览器中,counter通常并不是很高(通常为4位数字,有时为3位,有时为5位)。
我们也知道这是从规范中发生的,规范中写道:

now函数返回一个数字值,该数字值指定了UTC日期和时间的时间值,即“现在”的发生时间。

(我的强调) 因此,符合规范的实现不能在开始JavaScript“任务”之前决定now的值,然后在整个任务中重复使用它。这与规范相违背。

结论

由于我们知道对象初始化器中的属性初始化器是按顺序处理的,而且会调用两次Date.now,我们也知道两次Date.now 可能返回不同的值,因此我们知道对于var o = {a:Date.now(), b:Date.now()},在遵循规范的任何JavaScript引擎(例如NodeJS(V8)),o.a === o.b并不总是为真。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接