Even your second version of the comment is too what and not enough why. What comments are worse than no comments. They take up valuable space and become a liability if they're not kept in lock-step with the actual implementation.
As I've matured as a programmer, I've found that commenting everything is actually an anti-pattern. Every line of code is a liability that contributes to maintenance costs, even comments. Just as you should think about every executable line in your source code, you should put the same thought into all non-executable lines. I prefer to comment judiciously and try to write my thought process and anything that's likely to be confusing to help future developers understand the overall approach and intent of the code without trying to lay out the intent of every single line. Lines that are particularly complex get a comment, but anything simple stays uncommented, especially if it's idiomatic for the language.
If, to take your example, the code were:
let cartTotal = products.reduce(0, (total, product) => { total + product.price });
It probably does not need the comment you've listed. The names alone convey intent. If, however, you're were doing something like integer math, you would put a comment to the effect of
// cartTotal is computed in cents to avoid floating point math/rounding
// and must be divided by 100 to get the dollar amount.
That conveys information that is very difficult to extract from the code or the variable names.
I've also realized that, by far, the best comments I can leave are unit tests. They never become incorrect because they're forced to update with the code. When I'm trying to learn a new code base, I always start by running the tests and, if they pass, reading the tests before I read the code they test. There is no better way of understanding what use cases a developer thought were most important than the automated tests s/he felt were necessary. I love that new languages like Rust give developers the ability to combine documentation and tests and I hope many more languages follow that example.
The second best documentation are function/method/class/object/struct names. It's very important to get them right because they not only provide meaningful information in the place they're defined, they also provide useful information wherever they're used. Try to avoid the following:
/**
* Helpful comment
*/
class UnhelpfulName
...
The reason this is such a big problem is that it requires the developer or the IDE to find the comment whenever the name is referenced elsewhere in the source code. Names permeate throughout the codebase much better than comments. And if you lean too heavily on comments to provide meaning that the names do not, you end up having to write the same comments in many locations.