This proposal introduces a new method on arrays to push all elements from an iterable onto the end of the array.
Status: Stage 0
Champion: Daniel Rosenwasser (@DanielRosenwasser)
For more information see the TC39 proposal process.
Today, the most idiomatic and concise way to append multiple elements to an array from another iterable is to use Array.prototype.push with argument spread syntax (...arg):
const arr = [1, 2, 3];
const newElements = [4, 5, 6];
arr.push(...newElements);This pattern usually works, but it has some drawbacks.
One could argue that this idiom is not entirely obvious or discoverable, especially for developers who are new to JavaScript or unfamiliar with the spread syntax.
But the most critical issue with .push(...arg) is the risk of stack overflows when arg is a very large iterable, since the argument spread syntax will expand all elements into individual arguments.
This concern is not hypothetical; there have been real-world instances where this has caused runtime errors (for example, see this reported instance from Node.js users).
In these cases, pushing each individual element in a loop or forEach call may be used instead.
for (const item of newElements) {
arr.push(item);
}This code is arguably more explicit, but also more verbose for what feels like a common operation.
It also may not be optimized ideally in the case where new elements come from another array with a known size.
Once enough elements are push-ed, the array's backing storage may need to be resized, and cause intermediate reallocations.
Ideally, a runtime could reallocate exactly once if the new element count is known in the case of arrays, and known to exceed the available storage.
This proposal suggests adding a new method, Array.prototype.pushAll, which would allow for a more concise and readable way to append all elements from an iterable to an array.
The method could (roughly) be polyfillable as follows:
Array.prototype.pushAll = function(iterable) {
for (const item of iterable) {
this.push(item);
}
};Note
There are some edge cases to consider, such as when the array is modified during the operation (e.g. passing the same target array as its argument). The exact semantics of such cases might involve special-casing array inputs, but could be clarified in later stages.
With this new method, the previous example could be rewritten as:
const arr = [1, 2, 3];
const newElements = [4, 5, 6];
arr.pushAll(newElements);Many other languages have similar methods for appending multiple elements to growable list types. Below is a table that shows how several popular languages handle this operation:
| Language | Adding a Single Element | Adding Multiple Elements |
|---|---|---|
| Python | items.append(x) |
items.extend(new_values) |
| Java | items.add(x) |
items.addAll(newValues) |
| C# | items.Add(x) |
items.AddRange(newValues) |
| Ruby | items.push(x) |
items.concat(newValues) |
| C++ | items.push_back(x) |
items.insert(items.end(), newValues.begin(), newValues.end()) |
| Go | items = append(items, x) |
items = append(items, newValues...) |
| PHP | $array[] = $x |
$array_push($array, ...$newValues) |