Подтвердить что ты не робот

Странное поведение JavaScript в CodePen с массивными массивами

Следующий код выполняет молчащую логическую ошибку:

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

Возможное поведение (ы):

  • Переменная avg после второго цикла цикла должна быть удвоена, а длина массива должна быть 114 миллионов.

  • Я должен получить ошибку памяти.

Вывод при запуске как script:

  • avg Не меняется после второго цикла цикла.
  • Длина массива не 114 Mil, (Chrome 2-3M, Firefox Dev 5 Mil, MS Edge 788k).
4b9b3361

Ответ 1

Когда вы пишете код в Codepen - они фактически не выполняют его как есть, а скорее применяют к нему некоторые преобразования.

Они анализируют его в абстрактное синтаксическое дерево, находят циклы и вставляют инструкции явно, чтобы прекратить выполнение цикл, если прошло слишком много времени.

Когда вы выполните:

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++;
}

Вы можете увидеть это, проверив код кадра внутри самого кода.

Они вводят shouldStopLoop вызовы внутри вашего кода. У них есть script, называемый 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 [email protected]";
};

Если вы хотите запустить его самостоятельно - у JSBin есть аналогичная функциональность, и они открывают sourced it в качестве библиотеки защиты от цикла - до 500 LoC.

Ответ 2

Это просто codepen script ограничения для бегунов.

Я запускаю script в Chrome Developer Tools и в Node.JS REPL - все выглядит нормально.


Документы Codepen