console.log() 显示值实际更改之前变量的更改值

IT技术 javascript google-chrome variables
2021-01-18 12:23:45

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

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

6个回答

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

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

MDN 警告

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

这对我有用。对调试有很大帮助。只是好奇,这会在我每次登录时创建一个新对象吗?它基本上克隆了对象的当前状态,对吗?我想知道如果我在投入生产之前忘记删除这些日志记录功能,从长远来看是否会产生影响。
2021-03-10 12:23:45
@pokken 是的,所有这些都是创建对象的字符串副本。我不明白为什么在进入 prod 时离开日志功能会产生不利影响
2021-03-12 12:23:45
<script> console.log = function () { }; </script> 将此添加到生产中的 index.html 中,它将完全禁用 console.log。@pokken
2021-03-29 12:23:45

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

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

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

描述来自米奇克莱默 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(),不适用于实时控制台交互。

它已在 Webkit 中修复,但该修复程序尚未纳入 Chrome。Chrome 大约是在引入补丁的时候从 Webkit 分叉出来的。
2021-03-12 12:23:45
据我所知,这也是最新版 Firefox 中的一个问题(截至本评论有 53 个)。如果您尝试查看的对象需要您单击展开以查看其属性,则控制台输出将显示更新的变量,即使您在进行更改之前将其记录下来也是如此。例如:let test = [{a: 1}, {b: 2}]; console.log(test); test[0].xxx = 100; console.log(test);
2021-03-25 12:23:45
今天的部分实际上应该是第一位的。
2021-03-31 12:23:45
因此,开发人员不能仅仅打印所涉及的对象或数组,而是必须找到一种冗长且冗长的方式来在打印时打印该对象或数组的内容,因为 Chrome 开发人员太固执而无法实现这个补丁?彻底的疯狂!
2021-04-02 12:23:45
尽管已“修复”,但在 Chrome 46.0.2490.86 和 Qt 的 WebKit (Qt 5.5) 中,这个问题仍然发生在我身上。当对象的记录值在您身上发生变化时,会非常混乱现在我想我可能会尝试通过每次打印对象时做一个深拷贝来避免这个问题。
2021-04-07 12:23:45

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

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

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

自 2019 年或更长时间以来一直在 MDN 上的指导并没有使它成为“最新的”。
2021-03-15 12:23:45
谢谢!呃,在 javascript 中做基本事情的样板数量,而不会用脚射击自己......
2021-03-28 12:23:45
不,但正如我在回答中所说,它使它成为“来自 Mozilla 的最新指南”。
2021-03-30 12:23:45
感谢您参考文档。
2021-03-31 12:23:45
日志的全部意义不是基于在程序执行的逻辑流中的精确点保存现实快照的需要吗?因此,一旦程序结束,删除这些快照以支持任意的“无论最后的值”实际上是无稽之谈。
2021-04-03 12:23:45

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

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

说另一种方式:一个变量的值可以是一个原始值(数字,布尔值,null或字符串),或者它可以是一个参考的一个对象。字符串基元的情况有点奇怪,因为它们更像是对象而不是基元(标量)值,但它们是不可变的,所以可以假装它们就像数字一样。

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

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

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

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

@Pointy 那么,您如何解释在调用之前更改了元素的顺序.sort()
2021-03-14 12:23:45
我不知道; 我现在就去试试。当我尝试编辑时,它表明数组中的值实际上在排序前后是不同的。换句话说,在排序前记录 C[0] 显示它为 2,排序后 C[0] 显示为 1。
2021-03-17 12:23:45
2021-03-23 12:23:45
我不认为这真的会发生。如果是这样,那是由于有时奇怪的工作方式console.log()- 有时它与代码执行不完全同步。
2021-03-29 12:23:45
谷歌浏览器不会在记录时记录对象的状态。在 ie9 或 firefox 控制台中运行它。
2021-04-06 12:23:45