CodePen中奇怪的JavaScript行为与庞大的数组

时间:2016-04-05 11:53:44

标签: javascript

以下代码执行无提示逻辑错误:

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

可能的行为:

  • 第二个for循环后的变量avg应加倍,数组的长度应为1.14亿。

  • 我应该收到内存错误。

作为脚本运行时的输出:

  • avg在第二个for循环后不会更改。
  • 阵列长度不是114 Mil,(Chrome 2-3M,Firefox Dev 5 Mil,MS Edge 788k)。

2 个答案:

答案 0 :(得分:35)

当你在Codepen中编写代码时 - 他们实际上并没有按原样执行它,而是首先对它应用一些转换。

他们将其解析为abstract syntax tree,查找循环和insert instructions explicitly以便在过多时间后停止执行循环。

当你这样做时:

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内部的框架代码来看到这一点。

他们在您的代码中注入shouldStopLoop次调用。 他们有一个名为stopExecutionOnTimeout的脚本,它做了类似的事情(源自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具有类似的功能,它们有open sourced it作为循环保护库 - 低于500 LoC。

答案 1 :(得分:12)

它只是codepen脚本限制。

我在Chrome Developer ToolsNode.JS REPL中运行脚本 - 一切似乎都没问题。

Codepen docs