fill()

ES6+

Fills all the elements of an array from a start index to an end index with a static value.

Syntax

array.fill(value, start, end)

Parameters

value any

Value to fill the array with

start number optional

Start index (default: 0)

end number optional

End index (default: array.length)

Return Value

Array

The modified array

Examples

JavaScript
const arr = [1, 2, 3, 4];
arr.fill(0, 1, 3);
console.log(arr);
Output:
// [1, 0, 0, 4]

📌 When to Use

Use fill() to stamp a single static value across an array or a range of it: initializing counters and score tables to 0, building placeholder rows for skeleton-loading UIs (Array(8).fill(null) rendered as shimmering cards), resetting a game board between rounds via board.fill(0) without reallocating, or blanking a subrange with the start/end parameters. Its indispensable partner is the Array constructor: new Array(n) alone produces a sparse array of holes that map() and forEach() skip over, and .fill() is the idiomatic step that converts it into a dense, fully iterable array - which is why new Array(5).fill(0) is the standard "give me n real slots" incantation. The method's hard boundary is that it evaluates its argument ONCE: every slot receives the same value, which is exactly right for primitives and exactly wrong for objects, where all slots would share one mutable instance. The moment each slot needs its own object, computed value, or index-derived content, switch to Array.from({length: n}, (_, i) => ...), whose mapping function runs per slot. For filling with a sequence rather than a constant (0, 1, 2, ...), Array.from with (_, i) => i is likewise the tool - fill() is strictly for uniform values.

⚠️ Common Mistakes

Filling with an object or array literal: new Array(3).fill({count: 0}) evaluates the literal once and writes the SAME reference into all three slots, so arr[0].count++ appears to increment every element simultaneously. The symptom - "changing one item changes them all" - is among the most-asked JavaScript questions. Per-slot objects require Array.from({length: 3}, () => ({count: 0})).

Treating fill() as a copying method because it returns an array. The return value is the same mutated object, not a fresh one - const cleared = scores.fill(0) zeroes the original scores too, and any other holder of that reference sees its data wiped. When the source must survive, copy first or build a new filled array outright.

Building 2D grids with nested fill: new Array(3).fill(new Array(3).fill(0)) constructs ONE row array and points all three rows at it, so grid[0][0] = 1 "edits" column 0 of every row. Correct construction creates a distinct row per index: Array.from({length: rows}, () => new Array(cols).fill(0)).

Forgetting the end index is exclusive, as in slice(): arr.fill(0, 1, 3) writes to indices 1 and 2 only. Off-by-one range fills are easy to miss because nothing throws - the untouched cell simply keeps its old value and surfaces later as stale data.

Using new Array(n) WITHOUT fill() and then mapping: new Array(3).map(x => 0) returns three untouched holes because map() skips empty slots entirely. The fill() step is what makes the array iterable by the callback methods - or skip the dance entirely with Array.from({length: n}, () => 0).

✅ Best Practices

Draw the line by value type: fill() for primitives (numbers, strings, booleans, null), Array.from({length: n}, factory) whenever slots must hold objects, arrays, or per-index values. Following that split mechanically prevents the shared-reference bug from ever being written.

Reuse allocated arrays in hot code by refilling instead of reallocating: buffer.fill(0) resets a fixed-size numeric buffer in place, keeping the same backing store and element kind - friendlier to the GC and to V8's optimizer than constructing a fresh array every frame or tick.

Adopt new Array(n).fill(initialValue) as the standard fixed-size initializer - the fill() both sets the value and converts the constructor's holes into real elements, producing a packed array that every iteration method treats normally.

Use the range form for partial resets: scores.fill(0, startOfRound) zeroes from an index to the end, and fill(null, 2, 5) blanks a window while preserving both edges. Negative indices count from the end, matching slice() conventions.

For skeleton-loading UIs, Array(count).fill(null) provides exactly the right render fodder: n identical placeholder slots with no meaningful data, which the component maps into shimmer cards until the real records replace the array.

⚡ Performance Notes

fill() is O(k) over the filled range and writes in place with zero allocation - for numeric fills V8 can lower it to memset-style operations on packed arrays, making it the fastest way to initialize or reset bulk numeric storage short of using a TypedArray. new Array(n).fill(0) is also the performance-recommended constructor pattern: it allocates the backing store at final size once and immediately makes the array packed with a consistent element kind (PACKED_SMI for integers), the representation on which all subsequent array operations run fastest. Compared with alternatives at the million-element scale, fill(0) comfortably beats a manual assignment loop and Array.from({length: n}, () => 0), since the latter invokes a callback per slot; reserve Array.from for when per-slot computation is genuinely needed. For repeated resets of the same buffer (per-frame accumulators, histogram bins), refilling the existing array avoids garbage entirely, whereas reallocating each cycle creates collector pressure. If the data is purely numeric and large, Float64Array or Int32Array with fill() goes further still - fixed element kind, no boxing, predictable memory.

🌍 Real World Example

Creating a Grid or Matrix

Grid initialization is where fill()'s value semantics and reference semantics collide instructively, so this example shows the trap and the fix side by side. The one-dimensional fill of zeros is safe - numbers are copied by value. The WRONG grid fills three slots with one shared row array, so writing any cell appears to write the same column in every row; the CORRECT version uses Array.from so the factory runs once per row, manufacturing three independent arrays whose cells update individually. The final line demonstrates the range form, resetting only indices 1 through 3 of a scores array while the boundary values survive - the partial-reset pattern used between game rounds.

// Create a 5-element array of zeros
const zeros = new Array(5).fill(0);
// [0, 0, 0, 0, 0]

// WRONG: Creates rows that share the same array!
const wrongGrid = new Array(3).fill(new Array(3).fill(0));
wrongGrid[0][0] = 1;  // Changes ALL rows!

// CORRECT: Create unique row arrays
const grid = Array.from({length: 3}, () => new Array(3).fill(0));
grid[0][0] = 1;  // Only changes first row
// [[1,0,0], [0,0,0], [0,0,0]]

// Reset portion of an array
const scores = [10, 20, 30, 40, 50];
scores.fill(0, 1, 4);  // [10, 0, 0, 0, 50]

Related Methods