4

Is the order of iterating through an array using one of the native methods (map, forEach, reduce, filter, etc) deterministic and guaranteed by the standard?

EG, are foo, bar, baz, and qux guaranteed to be [0, 2, 6, 12]?

const a = [1, 2, 3, 4];
const foo = a.map((item, index) => item * index);
const bar = []; a.forEach((item, index) => bar[index] = item * index);
const baz = []; a.reduce((total, item, index) => baz[index] = item * index, 0);
const qux = []; a.filter((item, index) => qux[index] = item * index);
// etc

(These are (very) contrived examples)

matisetorm
  • 857
  • 8
  • 21
craigmichaelmartin
  • 6,091
  • 1
  • 21
  • 25
  • 3
    Yes, the numeric properties are traversed in ascending order. It's explicitly described in the language spec. – Pointy Aug 15 '17 at 19:06
  • btw, you need to use a start value for reduce, like `a.reduce((total, item, index) => baz[index] = item * index, null);` – Nina Scholz Aug 15 '17 at 19:09

1 Answers1

5

The callback function is called for each element present in the array, in ascending order. It is not called for missing elements. (Missing elements? Yes, JavaScript handle sparse arrays)

var test = [];
test[30] = 'Test'; // sparse array, only one element defined.

test.forEach(
  function(value){
    console.log(value); // will only be called one time.
  }
);

From the standard: ECMA-262

22.1.3.10 Array.prototype.forEach ( callbackfn [ , thisArg ] )

NOTE 1

callbackfn should be a function that accepts three arguments. forEach calls callbackfn once for each element present in the array, in ascending order. callbackfn is called only for elements of the array which actually exist; it is not called for missing elements of the array

If a thisArg parameter is provided, it will be used as the this value for each invocation of callbackfn. If it is not provided, undefined is used instead.

callbackfn is called with three arguments: the value of the element, the index of the element, and the object being traversed.

forEach does not directly mutate the object on which it is called but the object may be mutated by the calls to callbackfn.

When the forEach method is called with one or two arguments, the following steps are taken:

  1. Let O be ? ToObject(this value).
  2. Let len be ? ToLength(? Get(O, "length")).
  3. If IsCallable(callbackfn) is false, throw a TypeError exception.
  4. If thisArg was supplied, let T be thisArg; else let T be undefined.
  5. Let k be 0.
  6. Repeat, while k < len a. Let Pk be ! ToString(k). b. Let kPresent be ? HasProperty(O, Pk). c. If kPresent is true, then i. Let kValue be ? Get(O, Pk). ii. Perform ? Call(callbackfn, T, « kValue, k, O »). d. Increase k by 1.
  7. Return undefined.
some
  • 48,070
  • 14
  • 77
  • 93
  • That's `forEach`, but what about `map`? – nog642 Sep 15 '21 at 20:44
  • @nog642 What do you think? Did they specify completely different algorithms for *forEach*, *map*, *every*, *some*, etc.? There is a link in my answer, to the standard, where you can read the steps taken for any standard method defined on the prototype of the array, without me copying it to an old answer. – some Dec 12 '22 at 01:45