JavaScript日期变量赋值

18
var date1 = new Date();  
date1.setFullYear(2011, 6, 1);  

// 2011-07-01, ok  
console.log(date1);

// set date2 the same date as date1  
var date2 = date1;

// ...

// now I'm gonna set a new date for date2  
date2.setFullYear(2011, 9, 8);

// 2011-10-08, ok  
console.log(date2);

// 2011-10-08, wrong, expecting 2011-07-01  
// I didn't assign a new date to date1  
// WHY is date1 changed?  
console.log(date1);

1
看起来 date2 被设置为指向 date1 所指向的指针。 - James
为什么是2011年7月1日,如果你分配date1.setFullYear(2011, 6, 1);,那么2011年6月1日不正确吗? - Ahmed Elbatt
9个回答

30

日期是一个对象,因此它被赋值为引用——简单的方法是:

date2 = new Date( date1 );

8
当你执行以下操作时:
date2=date1;

你并没有创建一个新的Date对象,而是只是指向来自两个不同变量的日期对象。只有一个对象,因此无论您查看哪个变量,对其进行的任何更改都会显而易见。

让我们用一些ASCII艺术来说明:

date1 = new Date();

这给我们带来了:

+-------+
+ date1 +
+-------+                 +---------------+
| value |---------------->| 一个日期对象 |
+-------+                 +---------------+

现在当你执行以下操作时:

date2=date1;

我们有
+-------+
+ date1 |
+-------+
| value |------+
+-------+      |          +---------------+
               +--------->| 一个日期对象 |
               |          +---------------+
+-------+      |
+ date2 |      |
+-------+      |
| value |------+
+-------+
date1date2变量的value是指向Date对象的引用,而不是它的副本。(所有对象都是这样工作的。) 你可以将一个对象引用视为指向内存中对象位置的内存地址。 (它实际上取决于实现方式。)
这与基本数据类型不同,其中变量的值实际上包含原始数据,例如:
var n = 42;

结果为

+-----------+
+     n     |
+-----------+
| value: 42 |
+-----------+

(理论上而言。实际上,字符串“原始值”会表现得好像是这样的,但实际上可能更像对象存储。不过没关系,字符串是不可变的,并且对于字符串“原始值”,=====比较它们的内容,因此我们无法真正区分它们,并且可以假装它们实际上被变量包含。[仅仅为了让人困惑:JavaScript还有NumberString 对象,它们的行为类似于对象。])


关于你下面的问题:

在此期间,创建一个与预先存在的对象完全相同的第二个javascript对象的最有效方法是什么?

JavaScript对象没有通用的“克隆”操作,因此答案因对象而异。有些对象不需要克隆,因为它们是不可变的(不能被改变),因此不需要克隆(例如String对象)。

要克隆一个日期,很容易:

date2 = new Date(date1);

或者稍微更有效率一些的写法:

date2 = new Date(+date1);

(因为 + 会告诉 date1 对象将自己转换为数字,然后 Date 构造函数使用该数字。如果没有它,date1 对象将被要求将自己转换为字符串,然后 Date 构造函数将解析该字符串。仍然有效,但通过数字进行微调几乎肯定是过早的优化,并且可能会干扰引擎想要使用的任何隐藏优化。所以我只会选择 date2 = new Date(date1);。)

直接传递日期应该更快,因为它无论如何都会调用 ToPrimitive,所以 + 转换是多余的。不过这并不重要 :P - Esailija
1
@Esailija:你可能会这样想,不是吗?但是 Date 构造函数 调用没有提示的 ToPrimitiveToPrimitive 传递缺少提示调用 [[DefaultValue]],而 Date 的默认提示(与其他所有内容不同)是 String 而不是 Number。当然,实现可以自由优化...正如你所说,这并不重要。 - T.J. Crowder

2

这两个日期变量只是对同一个日期对象的引用。因此,您需要将date2克隆为date1的副本。更改为:

var date2 = date1;

转换为:

var date2 = new Date(date1.getTime());

2

JavaScript对于日期(以及所有非原始类型,例如var o = {}; var j = o; j.foo = 1; console.log(o.foo); //1)采用按引用传递的方式。另一方面,对于数字、字符串和布尔值,var o = 0; var j = o; j++; console.log(j); // 0,因此这是预期的行为。

如果您需要复制日期,您可以始终

var date2 = new Date( date1.getTime() );

* 请查看评论以了解为什么这并不完全正确。


1
这并不是真正的按引用传递。它是按值传递,其中值是引用。如果它是按引用传递,改变引用将会改变对象:a={}; b=a; b=null; 在按引用传递的求值模型中,a===null 应该为 true,因为 b 就是 a,而我们将 b 设置为 null - davin
@davin 这是我之前不知道的一个微妙之处。 - cwallenpoole
点赞。非常有帮助。不过有一个细节。如果date2已经被分配,那么就不需要使用new。可以通过以下方式完成赋值:date2.setTime(date1.valueOf()); - Karl

1

date2 是对 date1 的引用。

为了达到预期的结果,请执行以下操作:

var date1 = new Date();
date1.setFullYear(2011, 6, 1); 

var date2 = new Date();
date2.setTime(date1.valueOf());

1

这是@SergeS的答案的变体,但是在js中,Date()对象会强制转换为数字,因此您不需要使用getTime():

// general case
var dateValueCopy = new Date(date1);

并使用OP变量名称重述:

var date2 = new Date(date1);

1
解决方案是创建第二个日期,该日期使用与第一个日期相同的内部毫秒值进行复制:
var date1 = new Date();
var date2 = new Date(date1.getTime());

现在,您有两个独立的日期实例,可以相互独立地进行操作。

var date1 = new Date();
alert(date1); // Sat May 26 2012 11:26:16 GMT-0400 (EDT)
var date2 = new Date(date1.getTime());
date2.setMinutes(date2.getMinutes()+10);
alert(date1); // Sat May 26 2012 11:26:16 GMT-0400 (EDT)
alert(date2); // Sat May 26 2012 11:36:16 GMT-0400 (EDT)

更多阅读:日期 - MDN


1
似乎你可以通过直接将日期对象传递给日期构造函数来克隆日期对象。 - Esailija

0

你需要创建一个 date1 的副本,目前 date1date2 指向同一个日期对象。

var date2 = new Date(date1.valueOf());

0

当你这样做时:

date1 = new Date();  // <-- creates a new Date object
date2 = date1;       // <-- date2 gets passed a reference to the date1 object
                     //     making them effectively the same

你在第一行创建了一个日期对象,但第二行并没有创建另一个日期对象,它只是指向或引用已经创建的日期对象。这是许多面向对象编程语言中的常见特性。

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