admin管理员组

文章数量:1134247

This bit of code I understand. We make a copy of A and call it C. When A is changed C stays the same

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

But when A is an array we have a different situation. Not only will C change, but it changes before we even touch A

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

Can someone explain what happened in the second example?

This bit of code I understand. We make a copy of A and call it C. When A is changed C stays the same

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

But when A is an array we have a different situation. Not only will C change, but it changes before we even touch A

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

Can someone explain what happened in the second example?

Share Improve this question edited Feb 24, 2023 at 12:42 danronmoon 3,8735 gold badges35 silver badges58 bronze badges asked Jul 1, 2012 at 18:36 Frederik HFrederik H 1,5062 gold badges13 silver badges15 bronze badges 4
  • 5 If you're wondering about the sort seeming to be observable before it happens, make a shallow clone of the Array when logging. You'll see the actual result. console.log(C.slice()); A.sort(); console.log(C); Don't put too much trust in console representations of data. They're imperfect. – user1106925 Commented Jul 1, 2012 at 18:58
  • 1 @FrederikH Actually what you've described is a known bug that was patched for Webkit in August 2012 (not yet pulled into Google Chrome). See my answer for details. – Elliot B. Commented Sep 8, 2014 at 20:57
  • See also Is console.log() async or sync? – Bergi Commented Sep 13, 2015 at 13:01
  • 1 Or take a look at the even more famous console.log object at current state – RiZKiT Commented Apr 29, 2016 at 12:23
Add a comment  | 

8 Answers 8

Reset to default 125

console.log() is passed a reference to the object, so the value in the Console changes as the object changes. To avoid that you can:

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

MDN warns:

Please be warned that if you log objects in the latest versions of Chrome and Firefox what you get logged on the console is a reference to the object, which is not necessarily the 'value' of the object at the moment in time you call console.log(), but it is the value of the object at the moment you open the console.

Pointy's answer has good information, but it's not the correct answer for this question.

The behavior described by the OP is part of a bug that was first reported in March 2010, patched for Webkit in August 2012, but as of this writing is not yet integrated into Google Chrome. The behavior hinges upon whether or not the console debug window is open or closed at the time the object literal is passed to console.log().

Excerpts from the original bug report (https://bugs.webkit.org/show_bug.cgi?id=35801):

Description From mitch kramer 2010-03-05 11:37:45 PST

1) create an object literal with one or more properties

2) console.log that object but leave it closed (don't expand it in the console)

3) change one of the properties to a new value

now open that console.log and you'll see it has the new value for some reason, even though it's value was different at the time it was generated.

I should point out that if you open it, it will retain the correct value if that wasn't clear.

Response from a Chromium developer:

Comment #2 From Pavel Feldman 2010-03-09 06:33:36 PST

I don't think we are ever going to fix this one. We can't clone object upon dumping it into the console and we also can't listen to the object properties' changes in order to make it always actual.

We should make sure existing behavior is expected though.

Much complaining ensued and eventually it led to a bug fix.

Changelog notes from the patch implemented in August 2012 (http://trac.webkit.org/changeset/125174):

As of today, dumping an object (array) into console will result in objects' properties being read upon console object expansion (i.e. lazily). This means that dumping the same object while mutating it will be hard to debug using the console.

This change starts generating abbreviated previews for objects / arrays at the moment of their logging and passes this information along into the front-end. This only happens when the front-end is already opened, it only works for console.log(), not live console interaction.

Updated on March 9, 2023

The latest guidance from Mozilla as of March 2023:

Information about an object is lazily retrieved. This means that the log message shows the content of an object at the time when it's first viewed, not when it was logged. For example:

const obj = {};
console.log(obj);
obj.prop = 123;

This will output {}. However, if you expand the object's details, you will see prop: 123.

If you are going to mutate your object and you want to prevent the logged information from being updated, you can deep-clone the object before logging it. A common way is to JSON.stringify() and then JSON.parse() it:

console.log(JSON.parse(JSON.stringify(obj)));

There are other alternatives that work in browsers, such as structuredClone(), which are more effective at cloning different types of objects.

const mushrooms1 = {
  amanita: ["muscaria", "virosa"],
};

const mushrooms2 = structuredClone(mushrooms1);

mushrooms2.amanita.push("pantherina");
mushrooms1.amanita.pop();

console.log(mushrooms2.amanita); // ["muscaria", "virosa", "pantherina"]
console.log(mushrooms1.amanita); // ["muscaria"]

Previous Guidance from MDN

The latest guidance from Mozilla as of February 2023:

Don't use console.log(obj), use console.log(JSON.parse(JSON.stringify(obj))).

This way you are sure you are seeing the value of obj at the moment you log it. Otherwise, many browsers provide a live view that constantly updates as values change. This may not be what you want.

Arrays are objects. Variables refer to objects. Thus an assignment in the second case copied the reference (an address) to the array from "A" into "C". After that, both variables refer to the same single object (the array).

Primitive values like numbers are completely copied from one variable to another in simple assignments like yours. The "A++;" statement assigns a new value to "A".

To say it another way: the value of a variable may be either a primitive value (a number, a boolean, null, or a string), or it may be a reference to an object. The case of string primitives is a little weird, because they're more like objects than primitive (scalar) values, but they're immutable so it's OK to pretend they're just like numbers.

EDIT: Keeping this answer just to preserve useful comments below.

@Esailija is actually right - console.log() will not necessarily log the value the variable had at the time you tried to log it. In your case, both calls to console.log() will log the value of C after sorting.

If you try and execute the code in question as 5 separate statements in the console, you will see the result you expected (first, [2, 1], then [1, 2]).

Though it's not going to work in every situation, I ended up using a "break point" to solve this problem:

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

To ensure sensible in-browser debugging, it's necessary to clone the data before logging it.

This drop-in replacement for console.log() does just that -- and supports multiple arguments.

function logSnapshot() {
  let snapshot = []
  ;[...arguments].forEach(a => snapshot.push(structuredClone(a)))
  console.log.apply(null, snapshot)
}

Internally, it uses the structuredClone() global function to capture a point-in-time snapshot of each argument.

The issue is present in Safari as well. As others have pointed out in this and similar questions, the console is passed a reference to the object, it prints the value of the object at the time the console was opened. If you execute the code in the console directly for example, the values print as expected. Instead of JSON stringifying, I prefer to spread arrays (e.g. in your case console.log([...C]);) and objects: the result is quite the same, but the code looks a bit cleaner. I have two VS code snippets to share.

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

In order to get the same output as with console.log( JSON.parse(JSON.stringify(c))), you can leave out the string part if you wish. Incidentally, the spread syntax often saves time and code.

本文标签: