CodePen中奇怪的JavaScript行为与庞大的数组 [英] Strange JavaScript behavior in CodePen with humongous arrays
问题描述
以下代码执行无提示逻辑错误:
The following code performs a silent logical error:
const arr = [];
class Point{
constructor(){
this.x = Math.random() * 1000000;
this.y = Math.random() * 1000000;
}
}
console.time('foo');
let avg = 0;
for(let i = 0; i < 114000000; i++ ){
arr.push(new Point());
avg += arr[i].x / 1000;
}
console.log(avg, arr.length);
// shouldn't this double the avg ?
for(let i = 0; i < 114000000; i++ ){
avg += arr[i].x / 1000;
}
console.log(avg, arr.length);
console.timeEnd('foo');
CodePen - http://codepen.io/darkyen/pen/yOPMZg?editors=0010
CodePen - http://codepen.io/darkyen/pen/yOPMZg?editors=0010
可能的行为:
-
第二个for循环后变量
avg
应加倍并且长度为数组应该是1.14亿。
The variable
avg
after the second for loop should be doubled and The length of array should be 114 million.
我应该收到内存错误。
作为脚本运行时的输出:
Output when run as a script:
-
avg
在第二个for循环后不会改变。 - 阵列的长度不是114 Mil,(Chrome 2-3M,Firefox Dev 5 Mil,MS Edge 788k)。
avg
Does not change after the second for loop.- Length of the array is not 114 Mil, (Chrome 2-3M, Firefox Dev 5 Mil, MS Edge 788k).
推荐答案
当您在Codepen中编写代码时 - 它们实际上并不按原样执行,而是首先应用对它进行了一些转换。
When you write code in Codepen - they actually don't execute it as-is but rather first apply some transformations to it.
他们将其解析为抽象语法树
They parse it into an abstract syntax tree, find loops and insert instructions explicitly to stop executing the loop if too much time has passed.
你这样做:
for(let i = 0; i < 114000000; i++ ){
arr.push(new Point());
avg += arr[i].x / 1000;
}
您的代码运行如下:
for (var i = 0; i < 114000000; i++) {
if (window.CP.shouldStopExecution(1)) { // <- injected by Codepen!!!
break;
}
arr.push(new Point());
avg += arr[i].x / 1000;
iter++;
}
您可以通过检查CodePen内部的框架代码来看到这一点。
You can see this by inspecting the frame code inside CodePen itself.
他们在您的代码中注入 shouldStopLoop
调用。
他们有一个名为 stopExecutionOnTimeout
的脚本,它做类似这样的事情(源自Codepen):
They inject shouldStopLoop
calls inside your code.
They have a script called stopExecutionOnTimeout
which does something like this (source from Codepen):
var PenTimer {
programNoLongerBeingMonitored:false,
timeOfFirstCallToShouldStopLoop:0, // measure time
_loopExits:{}, // keep track of leaving loops
_loopTimers:{}, // time loops
START_MONITORING_AFTER:2e3, // give the script some time to bootstrap
STOP_ALL_MONITORING_TIMEOUT:5e3, // don't monitor after some time
MAX_TIME_IN_LOOP_WO_EXIT:2200, // kill loops over 2200 ms
exitedLoop:function(o) { // we exited a loop
this._loopExits[o] = false; // mark
},
shouldStopLoop:function(o) { // the important one, called in loops
if(this.programKilledSoStopMonitoring) return false; // already done
if(this.programNoLongerBeingMonitored)return true;
if(this._loopExits[o]) return false;
var t=this._getTime(); // get current time
if(this.timeOfFirstCallToShouldStopLoop === false)
this.timeOfFirstCallToShouldStopLoop = t;
return false;
}
var i= t - this.timeOfFirstCallToShouldStopLoop; // check time passed
if(i<this.START_MONITORING_AFTER) return false; // still good
if(i>this.STOP_ALL_MONITORING_TIMEOUT){
this.programNoLongerBeingMonitored = true;
return false;
}
try{
this._checkOnInfiniteLoop(o,t);
} catch(n) {
this._sendErrorMessageToEditor(); // send error about loop
this.programKilledSoStopMonitoring=false;
return true; // killed
}
return false; // no need
},
_sendErrorMessageToEditor:function(){/*... */
throw "We found an infinite loop in your Pen. We've stopped the Pen from running. Please correct it or contact support@codepen.io.";
};
如果你想自己运行它 - JSBin有类似的功能,它们有开源作为循环保护库 - 低于500 LoC。
If you want to run it yourself - JSBin has similar functionality and they have open sourced it as the loop-protect library - under 500 LoC.
这篇关于CodePen中奇怪的JavaScript行为与庞大的数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!