Скажите, если я ошибаюсь: array.forEach(callbackFunction) подходит для разреженных массивов. Он выполняет callbackFunction не для каждого индекса между нулем и длиной массива, а только для ключей, которые фактически находятся in массиве. И (скажите мне, если я ошибаюсь) эти ключи — именно то, что Object.keys(array) . Следовательно (скажите мне, почему я не прав) это не должно иметь значения, если метод .forEach вызывается для самого array или для Object.keys(array) . Итак, с какой стати такая разница в производительности — как если бы в одном случае выполнялся гигантский бессмысленный цикл от нуля до длины, но не в другом случае.

Фрагмент, показывающий разницу в производительности:

 function doNothing(){} CONSOLE = document.getElementById('console'); arr = []; arr[49888999] = 42; start = performance.now(); arr.forEach(doNothing); duration1 = performance.now() - start; start = performance.now(); Object.keys(arr).forEach(doNothing); duration2 = performance.now() - start; CONSOLE.textContent = [duration1, duration2].join('n'); 
 {amp}lt;pre id='console'{amp}gt;{amp}lt;/pre{amp}gt; 

Фрагмент, показывающий, что функция обратного вызова вызывается только один раз в обоих случаях

 console1 = document.getElementById('console1'); console2 = document.getElementById('console2'); function doNothingVerbose1(){ console1.textContent = 1   ( console1.textContent); } function doNothingVerbose2(){ console2.textContent = 1   ( console2.textContent); } arr = []; arr[49888999] = 42; start = performance.now(); arr.forEach(doNothingVerbose1); duration1 = performance.now() - start; start = performance.now(); Object.keys(arr).forEach(doNothingVerbose2); duration2 = performance.now() - start; console.log(duration1, duration2); 
 ~~~~~ 1 ~~~~~ {amp}lt;pre id='console1'{amp}gt;0{amp}lt;/pre{amp}gt; ~~~~~ 2 ~~~~~ {amp}lt;pre id='console2'{amp}gt;0{amp}lt;/pre{amp}gt; 

ОБНОВИТЬ

Я только что сделал тест, чтобы выяснить, действительно ли вышеупомянутый arr=[];arr[49888999]=42; является фактическим разреженным массивом, то есть имеет гораздо меньший объем памяти по сравнению с выполнением arr=new Array(49889000) . И да, это так. Делая это сотни раз в цикле, разреженная версия занимает пару секунд, но не падает, но new Array(50 million) версия new Array(50 million) дает сбой скрипке. Так что, если он не хранится как «обычный массив C » в движке, то движок должен «иметь» Object.keys массива, так почему же движок не использует его эффективно? Я мог бы иметь слишком упрощенное представление о том, что должен делать движок JS; Неправильно ли говорить, что движок должен «иметь» Object.keys потому что он «имеет» реализацию разреженного массива, каким-то образом поддерживающую нашу переменную arr ? Может быть, кто-то, действительно работающий над браузером / движком JS, сможет пролить свет на это.

не отвечая на вопрос «почему» здесь — за исключением того, что я думаю, что Кайидо, скорее всего, спекулирует точно на цели (см. комментарии ниже вопроса).

Возможно, более интересный вопрос — как бороться с этим явлением на практике. Если, например, мне придется иметь дело с «разреженным массивом», как в «2 позициях продукта 51472 и 1 позиции продукта 81369», я буду использовать объект ( {} ) с ключами 51472 и 81369, а не массив ( [] ).

Делать его массивом только потому, что все ключи являются неотрицательными целыми числами, — плохая идея, потому что тогда у вас есть .forEach , который является ложным другом

связанный с этим вопрос: почему мы можем создавать разреженные массивы в JavaScript?

Чтобы ответить на ваш вопрос почему :


Согласно документации ECMA :

  1. Метод .forEach будет перебирать все элементы массива по свойству .length .
  2. Обратный вызов, переданный в .forEach будет вызван, только если элемент не пуст.

Чтобы продемонстрировать это, вы можете просто сделать:

 function doNothing(){} let perf; console.log('Array with 50 million length and 1 non-empty element:'); const a = []; a[49999999] = 'a'; console.log('a.length:', a.length); perf = performance.now(); a.forEach(doNothing); console.log('a:', performance.now() - perf); console.log(''); console.log('Array with 0 length:'); const b = []; b.foo = 'a'; console.log('b.length:', b.length); perf = performance.now(); b.forEach(doNothing); console.log('b:', performance.now() - perf); console.log(''); console.log('Array with 50 million length and 0 non-empty element:'); const c = []; c.length = 50000000; console.log('c.length:', c.length); perf = performance.now(); c.forEach(doNothing); console.log('c:', performance.now() - perf);