5 ms·
Another trick that might be more practical for actually debugging is using the object shorthand. For example, instead of... console.log(x, y); which conta
by jchw 6y ago
Another trick that might be more practical for actually debugging is using the object shorthand. For example, instead of...
console.log(x, y);
which contains the information you need, but lacks any useful context, try...
console.log({x, y});
...which will print out like an object, including the key names.
- tshaddox 6y agoUnfortunately that tends to print the object with collapsed values, requiring you to expand the object to actually see any of the values.
- ollien 6y agoI much prefer it being collapsed. When dealing with large objects, the firehose can be annoying.
- pjerem 6y agoThen, one day, after 12 hours chasing a race condition bug, you learn that when you expand objects from console.log, it shows you the current value of properties and not the values at the time of the console log. Because the values are evaluated when you click on the arrow. Then you quit web development and start a new career.
- matja 6y agoI was just about to mention this, I wasted a solid hour or two on this until I realised what was happening.
- brundolf 6y agoAs I pointed out in another comment: console.log({x, y}); the wrapping object is being created at log time, so its values will never be changed after the fact. That could still happen with the contents of x or y themselves, but then it's no different from the original way (console.log(x, y);)
- evan_ 6y agoWe can actually just test this. Here's some code: const x = {value: 0}; console.log(x); console.log({x}); x.value = 1; Running that in the latest Chrome javascript console, we see that the first version prints `{value: 0}` and the second prints `{x: {...}}`. When you expand the second one, it will show `{x: {value: 1}}`.
- Lex-2008 6y agoyep, but then when I expand (click triangle) the {value: 0} line, it shows "value: 1" on the next line, only to rise the confusion. To be fair, there is also an "i" in a square, reminding me about this behaviour.
- Folcon 6y agoThe really fun part is doing this :)... Evaluate the expression: const x = {value: 0}; console.log(x); console.log({x}); x.value = 1; You get this: {value: 0} {x: {…}} Expand the first arrow of the `x:`: v {x: {…}} > x: {value: 1} Now evaluate: x.value = 3 Then expand the second arrow: v {x: {…}} > x: value: 3 Now if you unexpand the arrow, you get 1, but if you expand it you get 3 =)... (Well at least in chrome)
- Lex-2008 6y ago> Now if you unexpand the arrow, you get 1, but if you expand it you get 3 =)... Moreover, if you expand arrow next to {value: 0}, you will see literally this: v {value: 0} [i] value: 3
- brundolf 6y ago> its values will never be changed after the fact By this I mean, the x and y in the output will never themselves change value. They may be mutated, but they cannot be reassigned in the printed object. The printed object is exclusively referenced by the console itself, even if the nested objects within it may be referenced elsewhere. > That could still happen with the contents of x or y themselves, but then it's no different from the original way (console.log(x, y);) By this I mean exactly what you demonstrated, the point being that it had nothing to do with the original suggestion made by jchw.
- newsbinator 6y agoRite of passage for every web developer. The moment when you realize this is the moment when the universe makes sense again... after long hours of total confusion.
- drinchev 6y agoYeah I don't remember how I found about this, but I remember it involved some `console.log(JSON.stringify(...))`. It's tricky to notice.
- randomsearch 6y agoYou are not wrong. Is this a repl or a debugger? Because it seems to be mixture? How is that helpful?!
- TeMPOraL 6y agoFWIW, it's not like it could work in any other way. When you console.log() an object, it just stores a pointer to the object, and decorates it with an interactive label containing some text, so that it looks nice. This is fast to do, and most of the time, it's exactly what you want. To log a static snapshot with equivalent interactive expansion capability, console.log() would have to do a general deep copy of your object - it would have to walk every pointer in the object and replace the pointee with its own recursive full deep copy, making sure to detect and handle cycles well, and keeping track of every replacement to ensure referential integrity (e.g. if two random objects A and B both have a pointer to the same object C, the copied A' and B' better both point at the same C'). An unlucky console.log() could easily copy half of your heap into the console, and god help you if you logged a DOM element. (Also, all these copies would not be garbage-collected until you cleared the console.) An universal deep copy is impractical to implement (notice how nobody seems to ever implement it, at all, in any programming language), and having console.log() do one would be an incredibly powerful and unpredictable footgun. Meanwhile, if you want to log a static snapshot of an object, all you need to do is to write console.log(cloneForLog(object)), where cloneForLog() is a function you wrote that does whatever copying is appropriate in your situation. I think the only bad thing about console.log() is that this behavior is not taught to people as a core and important aspect of the function. I guess maybe if console.log() was restricted to strings, and something like console.logPresentation() was a separate function for printing objects, people would check the docs first and wouldn't be surprised.
- anyfoo 6y ago> To log a static snapshot with equivalent interactive expansion capability, console.log() would have to do a general deep copy of your object Not necessarily, and if it did I don't think it would address the actual problem. console.log could do all the presentation work upfront, right then and there when you log, and provide a collapsed view of that. This would have to include detecting and cycling handles, as it would have to do for a deep copy as you mentioned. The cost would be wasting cycles on this work even if nobody looks at its result. But where it gets dangerous is if there's any side effects in following the object tree. If visiting for logging e.g. creates nodes, or changes them, or whatever. Arguably "you get what you ask for", but this and the performance hit for generating log output before anyone really looks at it deeply, are probably the main reason things are as they are.
- spondyl 6y agoExcuse me?! I just tried this out and I honestly never knew this until now. I can only imagine how much this has caused me trouble in the past.
- brundolf 6y agoIf they're primitives most consoles will show you the values without expansion
- dekerta 6y agoYou could do console.table({x, y}) if you really want to see them initially expanded
- JonathonW 6y agoOn Node, console.dir(x, {depth: null}) will print the object fully expanded (even in cases where console.log by itself won't). Pass something other than `null` to `depth` and it'll recurse to that depth, as well. Doesn't help on browser, though; the options arg to console.dir isn't part of the standard there.
- josephg 6y agoYou can console.log(JSON.stringify(x, null, 2)) to pretty-print the object as JSON. It doesn’t look as good, but it’ll expand and show you the value as it was the moment you logged it. (As opposed to the value when you open it in dev tools)
- dylan604 6y agothank you. hadn't seen that before
- thinkingkong 6y agoOr you can console.log(JSON.stringify(x, null, 2));
- brundolf 6y agoI think you misunderstood the intent of the GP
- matja 6y agoBut this way is easier to reconcile the output, because the values logged shown are what they where at the time of the console.log(), not at the time of expansion (later). Try this in a browser console: x={a:1,b:{c:1}};console.log(x);x.b.c=2; then 'expand' the object, 'c' will be logged as 2, not 1
- brundolf 6y agoThe original commenter was creating the object at log-time, though, which means it wouldn't be shared by anything else. Unless x or y is an object, but in that case the issue is completely tangential to the original suggestion And anyway- JSON.parse(JSON.stringify(x)) would be preferable because you'd still get the browser's rich object exploration
- da39a3ee 6y ago> Unless x or y is an object, but in that case the issue is completely tangential to the original suggestion Of course we have to assume that x and y may be objects!
- brundolf 6y agoPosted this and then realized you beat me to it :)
- tomduncalf 6y agoYes, I love this one!
- bobbylarrybobby 6y agoI really like this method but unfortunately most debuggers print objects with keys in alphabetical order so there’s no way to get the keys you care about most at the top. Is there a way to rectify this?
- aardvarkr 6y agoThis is going to make it significantly more janky but why not just put it into a list inside the curly brackets? That should preserve order while still triggering the object debugging feature
- ljm 6y agoThe best trick for actual debugging is to leverage your browser console. In a local build you basically have a 1:1 mapping of source code to deployed code and the debugger here basically becomes your IDE. You can hit Ctrl+Shift+P in the inspector and use the same kind of fuzzy file matcher you have in Sublime Text or VS Code, and from there you can set breakpoints, modify the code in memory, and so on. The console will reflect on the entire scope and annotate the code with their runtime values, the same as you get when working inside a JetBrains IDE. But it's JS, so you can tweak it without committing it to disk and so you get some form of REPL driven development. I can't remember the last time I've used a console.log over setting a breakpoint in the debugger and fucking with the application state at that point to understand an issue. You can get quite close to that after you've bundled your code and deployed it to a server, so long as you've got good source maps going on. Print debugging is invaluable, but I can't help but think there's something of Smalltalk or Lisp in how you can mess with your app within a sandbox through the inspector, at runtime. The only thing that breaks the model is the transpilation and minification, without sourcemaps.
- meetups323 6y agoFYI VS Code's JS debugger speaks Chrome Debugger Protocol and allows you to use the built in Debug Console just as you would the browser's debug console, also it's possible to set browser breakpoints/etc., and you get inline value hovers.
- ConnorLeet 6y agoMost IDEs, for that matter!
- quickthrower2 6y agoI bet the last time you used console.log was a race condition bug then :-)