92

这段代码我明白了。我们复制 A 并将其称为 C。当 A 更改时,C 保持不变

var A = 1;
var C = A;
console.log(C); // 1
A++;
console.log(C); // 1

但是当 A 是一个数组时,我们有不同的情况。不仅C会改变,而且在我们接触A之前它也会改变

var A = [2, 1];
var C = A;
console.log(C); // [1, 2]
A.sort();
console.log(C); // [1, 2]

有人可以解释第二个例子中发生了什么吗?

4

7 回答 7

84

Console.log()传递了对对象的引用,因此控制台中的值会随着对象的变化而变化。为避免这种情况,您可以:

console.log(JSON.parse(JSON.stringify(c)))

MDN 警告

请注意,如果您在最新版本的 Chrome 和 Firefox 中记录对象,您在控制台上记录的内容是对该对象的引用,这不一定是您调用时对象的“值” console.log(),而是它是您打开控制台时对象的值。

于 2018-02-25T02:21:56.800 回答
47

Pointy 的答案有很好的信息,但它不是这个问题的正确答案。

OP 描述的行为是 2010 年 3 月首次报告的错误的一部分,并于 2012 年 8 月为 Webkit 进行了修补,但在撰写本文时尚未集成到 Google Chrome 中。该行为取决于将对象文字传递给时控制台调试窗口是打开还是关闭console.log()

原始错误报告( https://bugs.webkit.org/show_bug.cgi?id=35801 )的摘录:

来自 mitch kramer 的描述 2010-03-05 11:37:45 PST

1) 创建一个具有一个或多个属性的对象字面量

2)console.log 该对象但保持关闭(不要在控制台中展开它)

3) 将其中一个属性更改为新值

现在打开那个console.log,你会看到它出于某种原因有新的价值,即使它的价值在它生成的时候是不同的。

我应该指出,如果您打开它,如果不清楚,它将保留正确的值。

Chromium 开发人员的回复:

评论 #2 来自 Pavel Feldman 2010-03-09 06:33:36 PST

我认为我们永远不会解决这个问题。我们无法在将对象转储到控制台时对其进行克隆,我们也无法监听对象属性的更改以使其始终真实存在。

我们应该确保现有的行为是预期的。

随之而来的是很多抱怨,最终导致了错误修复。

2012 年 8 月实施的补丁的变更日志注释 ( http://trac.webkit.org/changeset/125174 ):

截至今天,将对象(数组)转储到控制台将导致对象的属性在控制台对象扩展时被读取(即延迟)。这意味着在改变它的同时转储同一个对象将很难使用控制台进行调试。

此更改在记录时开始为对象/数组生成缩写预览,并将此信息传递到前端。这仅在前端已经打开时发生,它仅适用于 console.log(),不适用于实时控制台交互。

于 2014-06-12T04:51:05.057 回答
23

Mozilla截至 2022 年 2 月的最新指南:

不使用console.log(obj),使用console.log(JSON.parse(JSON.stringify(obj)))

这样,您就可以确定您在obj记录时看到了 的价值。否则,许多浏览器会提供实时视图,该视图会随着值的变化而不断更新。这可能不是你想要的。

于 2019-11-15T23:10:32.170 回答
13

数组是对象。变量的是对象。因此,第二种情况下的赋值将数组的引用(地址)从“A”复制到“C”。之后,两个变量都引用同一个对象(数组)。

像数字这样的原始值在像您这样的简单赋值中完全从一个变量复制到另一个变量。“A++”;语句为“A”分配一个新值。

换一种说法:变量的值可以原始值(数字、布尔值、null. 或字符串),也可以是对对象的引用。字符串原语的情况有点奇怪,因为它们更像对象而不是原始(标量)值,但它们是不可变的,所以可以假装它们就像数字一样。

于 2012-07-01T18:38:09.307 回答
4

编辑:保留这个答案只是为了保留下面的有用评论。

@Esailija 实际上是正确的 -console.log()不一定会记录变量在您尝试记录它时所具有的值。在您的情况下,两次调用都console.log()将记录排序C 后的值。

如果您尝试在控制台中将相关代码作为 5 个单独的语句执行,您将看到预期的结果(首先,[2, 1],然后[1, 2])。

于 2012-07-01T18:41:11.473 回答
2

虽然它不会在所有情况下都有效,但我最终使用了一个“断点”来解决这个问题:

mysterious = {property:'started'}

// prints the value set below later ?
console.log(mysterious)

// break,  console above prints the first value, as god intended
throw new Error()

// later
mysterious = {property:'changed', extended:'prop'}
于 2012-08-26T04:24:02.283 回答
0

该问题也存在于 Safari 中。正如其他人在这个问题和类似问题中指出的那样,控制台传递了一个对对象的引用,它在控制台打开时打印对象的值。例如,如果您直接在控制台中执行代码,则值会按预期打印。我更喜欢传播数组(例如,在您的情况下为console.log([...C]);)和对象,而不是 JSON 字符串化:结果完全相同,但代码看起来更简洁。我有两个VS 代码片段要分享。

    "Print object value to console": {
      "prefix": "clo",
      "body": [
         "console.log(\"Spread object: \", {...$0});"
      ],
      "description": "Prints object value instead of reference to console, to avoid console.log async update"
   },
   "Print array value to console": {
      "prefix": "cla",
      "body": [
         "console.log(\"Spread array: \", [...$0]);"
      ],
      "description": "Prints array value instead of reference to console, to avoid console.log async update"
   }

为了获得与console.log( JSON.parse(JSON.stringify(c)))相同的输出,如果您愿意,可以省略字符串部分。顺便说一句,扩展语法通常可以节省时间和代码。

于 2019-10-12T17:56:03.003 回答