├── .gitignore └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | # Only apps should have lockfiles 40 | yarn.lock 41 | package-lock.json 42 | npm-shrinkwrap.json 43 | pnpm-lock.yaml 44 | 45 | # Build directory 46 | build 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `Array.prototype.pushAll` 2 | 3 | This proposal introduces a new method on arrays to push all elements from an iterable onto the end of the array. 4 | 5 | ## Status 6 | 7 | Status: Stage 0 8 | 9 | Champion: Daniel Rosenwasser (@DanielRosenwasser) 10 | 11 | For more information see the [TC39 proposal process](https://tc39.github.io/process-document/). 12 | 13 | ## Motivation 14 | 15 | 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`): 16 | 17 | ```js 18 | const arr = [1, 2, 3]; 19 | const newElements = [4, 5, 6]; 20 | arr.push(...newElements); 21 | ``` 22 | 23 | This pattern usually works, but it has some drawbacks. 24 | 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. 25 | 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. 26 | 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](https://github.com/nodejs/node/issues/16870)). 27 | 28 | In these cases, `push`ing each individual element in a loop or `forEach` call may be used instead. 29 | 30 | ```js 31 | for (const item of newElements) { 32 | arr.push(item); 33 | } 34 | ``` 35 | 36 | This code is arguably more explicit, but also more verbose for what feels like a common operation. 37 | It also may not be optimized ideally in the case where new elements come from another array with a known size. 38 | Once enough elements are `push`-ed, the array's backing storage may need to be resized, and cause intermediate reallocations. 39 | 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. 40 | 41 | ## Proposed Solution 42 | 43 | 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. 44 | The method could (roughly) be polyfillable as follows: 45 | 46 | ```js 47 | Array.prototype.pushAll = function(iterable) { 48 | for (const item of iterable) { 49 | this.push(item); 50 | } 51 | }; 52 | ``` 53 | 54 | > [!NOTE] 55 | > 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). 56 | > The exact semantics of such cases might involve special-casing array inputs, but could be clarified in later stages. 57 | 58 | With this new method, the previous example could be rewritten as: 59 | 60 | ```js 61 | const arr = [1, 2, 3]; 62 | const newElements = [4, 5, 6]; 63 | arr.pushAll(newElements); 64 | ``` 65 | 66 | ## Existing Precedent 67 | 68 | Many other languages have similar methods for appending multiple elements to growable list types. 69 | Below is a table that shows how several popular languages handle this operation: 70 | 71 | **Language** | **Adding a Single Element** | **Adding Multiple Elements** 72 | :--------|-----------------------------------------|------------------------- 73 | Python | [`items.append(x)`][python-append] | [`items.extend(new_values)`][python-extend] 74 | Java | [`items.add(x)`][java-add] | [`items.addAll(newValues)`][java-addall] 75 | C# | [`items.Add(x)`][csharp-add] | [`items.AddRange(newValues)`][csharp-addrange] 76 | Ruby | [`items.push(x)`][ruby-push] | [`items.concat(newValues)`][ruby-concat] 77 | C++ | [`items.push_back(x)`][cpp-push-back] | [`items.insert(items.end(), newValues.begin(), newValues.end())`][cpp-insert] 78 | Go | [`items = append(items, x)`][go-append] | [`items = append(items, newValues...)`][go-append] 79 | PHP | [`$array[] = $x`][php-assign-end] | [`$array_push($array, ...$newValues)`][php-array-push] 80 | 81 | [python-append]: https://docs.python.org/3.14/library/stdtypes.html#list.append 82 | [python-extend]: https://docs.python.org/3.14/library/stdtypes.html#list.extend 83 | [csharp-add]: https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.add?view=net-9.0#system-collections-generic-list-1-add(-0) 84 | [csharp-addrange]: https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.addrange?view=net-9.0#system-collections-generic-list-1-addrange(system-collections-generic-ienumerable((-0))) 85 | [java-add]: https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/util/ArrayList.html#add(E) 86 | [java-addall]: https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/util/ArrayList.html#addAll(java.util.Collection) 87 | [go-append]: https://pkg.go.dev/builtin#append 88 | [cpp-push-back]: https://en.cppreference.com/w/cpp/container/vector/push_back 89 | [cpp-insert]: https://en.cppreference.com/w/cpp/container/vector/insert 90 | [ruby-push]: https://docs.ruby-lang.org/en/3.4/Array.html#method-i-push 91 | [ruby-concat]: https://docs.ruby-lang.org/en/3.4/Array.html#method-i-concat 92 | [php-assign-end]: https://www.php.net/manual/en/language.types.array.php#language.types.array.syntax.modifying 93 | [php-array-push]: https://www.php.net/manual/en/function.array-push.php --------------------------------------------------------------------------------