The difference is that the forEach closure is writing to a bound variable. If you do the same thing in the for loop, it gets slow, too:
{
console.time('for 1')
let sum = 0
for (let i = 0; i < data.length; i++) {
sum += data[i] * data[i];
}
console.timeEnd('for 1')
}
{
console.time('for 2')
let sum = 0
for (let i = 0; i < data.length; i++) {
sum += data[i] * data[i];
}
console.timeEnd('for 2')
function foo() { sum = 1; } // create a closure bound to sum; note we never even call this
}
Result:
for 1: 31.060ms for 2: 789.796ms
Note that the loops are identical. The only difference is that in the second block, we've bound sum to a closure. That makes writing to it much more expensive.
That's why your forEach loop is so much slower. It's the only code in your example that writes to a bound variable inside the loop. You'd get the same performance degradation in any of your other looping constructs if you do the same thing.