【问题标题】:console.log() shows the changed value of a variable before the value actually changesconsole.log() 在值实际改变之前显示变量的改变值
【发布时间】:2012-07-02 08:03:09
【问题描述】:

这段代码我理解。我们复制 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]

谁能解释第二个例子中发生了什么?

【问题讨论】:

  • 如果您想知道排序在发生之前似乎是可观察的,请在记录时对数组进行浅层克隆。你会看到实际的结果。 console.log(C.slice()); A.sort(); console.log(C); 不要太相信 console 的数据表示。它们并不完美。
  • @FrederikH 实际上,您所描述的是一个已知的错误,该错误已在 2012 年 8 月为 Webkit 进行了修补(尚未纳入 Google Chrome)。详情见我的回答。
  • 或者看看更出名的console.log object at current state
  • 也许使用 console.table() ?

标签: javascript google-chrome variables


【解决方案1】:

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

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

MDN warns:

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

【讨论】:

  • 这对我有用。对调试有很大帮助。只是好奇,这会在我每次登录时创建一个新对象吗?它基本上克隆了对象的当前状态,对吗?我想知道如果我在投入生产之前忘记删除这些日志记录功能,从长远来看是否会影响。
  • @pokken 是的,这一切都是为您的对象创建一个字符串副本。我不明白为什么在进入 prod 时离开日志记录功能会产生不利影响
  • 在生产环境中将其添加到 index.html 中,它将完全禁用 console.log。 @pokken
【解决方案2】:

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

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(),不适用于实时控制台交互。

【讨论】:

  • 尽管被“修复”,但这个问题仍然发生在我身上,无论是在 Chrome 46.0.2490.86 中还是在 Qt 的 WebKit (Qt 5.5) 中。当一个对象的记录值 change 在您身上时,您会感到非常困惑。现在我想我可以尝试通过在每次打印对象时对对象进行深层复制来避免这个问题。
  • 它已在 Webkit 中修复,但尚未将修复程序拉入 Chrome。 Chrome 大约是在引入补丁时从 Webkit 分叉出来的。
  • 因此,开发人员不能只打印所涉及的对象或数组,而是必须找到一种冗长且样板的方式来在打印时打印该对象或数组的内容,只需因为 Chrome 开发人员太固执而无法为此实施补丁?彻底的疯狂!
  • 据我所知,这也是最新 Firefox 中的一个问题(截至本评论为 53 个)。如果您要查看的对象要求您单击展开以查看其属性,控制台输出将显示更新后的变量,即使您在进行更改之前记录它也是如此。例如:let test = [{a: 1}, {b: 2}]; console.log(test); test[0].xxx = 100; console.log(test);.
  • as of today 部分实际上应该排在第一位。
【解决方案3】:

Mozilla 截至 2021 年 12 月的最新指南:

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

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

【讨论】:

  • 谢谢!哎呀,在 JavaScript 中做基本的事情,而不是在脚下射击自己的样板数量......
  • 日志记录的全部意义不是基于在程序执行的逻辑流程中的精确点保存现实快照的需要吗?因此,一旦程序结束,删除这些快照以支持任意的“无论最后的值是什么”实际上是无稽之谈。
  • 该指南自 2019 年或更长时间以来一直在 MDN 上,并不意味着它是“最新的”。
  • 不,但正如我在回答中所说,它使它成为“Mozilla 的最新指南”。
  • 感谢您参考文档。
【解决方案4】:

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

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

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

【讨论】:

  • 那么没有办法打印出一个数组到控制台,修改数组,然后打印出修改后的版本?
  • @Nate 是的,有;我不太确定我的回答中有什么令人困惑的地方。原始问题中的第二个示例可能是 console.log 工作方式固有的延迟的副作用。根据我的经验,Chrome 的开发者控制台在这方面是最成问题的。
  • 对不起,我误读了原来的问题。我遇到的问题是,当我打印出一个数组时,使用splice() 删除元素,然后再次打印出来,拼接的版本被打印出来两次(即使第一个打印语句在拼接之前)。我应该更仔细地阅读 OP 的问题。
  • @Nate OK - 根据我的经验,Chrome 是最糟糕的。我从来没有找到让它表现得更好的方法,但我也没有真正尝试过。
【解决方案5】:

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

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

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

【讨论】:

  • 我认为这不会真的发生。如果是这样,那是因为console.log() 的工作方式有时很奇怪——有时它与代码执行不完全同步。
  • @Pointy 那你怎么解释在调用.sort()之前改变了元素的顺序?
  • 我不知道;我现在要试试。 edit 当我尝试它时,它表明数组中的值实际上在排序之前和之后是不同的。换句话说,在排序前记录 C[0] 显示为 2,排序后 C[0] 显示为 1。
  • google chrome 不会记录对象在记录时的状态。在 ie9 或 firefox 控制台中运行。
【解决方案6】:

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

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'}

【讨论】:

    【解决方案7】:

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

        "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))) 相同的输出,您可以根据需要省略字符串部分。顺便说一句,spread syntax 通常可以节省时间和代码。

    【讨论】:

      猜你喜欢
      • 2022-11-02
      • 2016-07-10
      • 2021-06-26
      • 1970-01-01
      • 2013-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多