Array Size in JavaScript: A Visual Guide

Aug 28, 2026·21 min read

You have an array of four scores and need to know its size. scores.length returns 4, but that answer changes meaning once the array contains empty slots or you need to count only scores that passed.

Use array.length for an array’s index range, count occupied slots separately when holes matter, and use a concrete callback such as filter(score => score >= 10).length when size means values matching a condition.

How to Get an Array’s Size

For an ordinary JavaScript array, read its length property. There are no parentheses because length is a property, not a method:

const emptyScores = [];
const scores = [18, 12, 20, 15];

console.log(emptyScores.length);
console.log(scores.length);
0
4

The empty array has a length of 0. The second array has four values at indexes 0, 1, 2 and 3, so its length is 4.

JavaScript arrays do not provide a standard .size property or .size() method. Reading a missing property produces undefined, while trying to call it as a function throws:

const scores = [18, 12, 20, 15];

console.log(scores.size);
// scores.size(); // TypeError
undefined

Use this form:

const size = scores.length;

Not these:

const size = scores.size;
const size = scores.size();

For a dense array, where every index from 0 through the final index contains an element, length also tells you how many elements the array holds. That familiar case makes the rule look simpler than it is.

Holes change the answer.

Arrays covers the wider set of operations for adding, removing and transforming values. Here, the important part is deciding what size means before choosing the expression that calculates it.

What Array Length Actually Counts

An array’s length describes its index range. It is normally one greater than the highest array index.

Indexes start at zero, so three values occupy indexes 0, 1 and 2:

const cities = ['Lima', 'Oslo', 'Kyoto'];

console.log(cities[0]);
console.log(cities[2]);
console.log(cities.length);
Lima
Kyoto
3

The last index is 2, but the length is 3. That one-step difference comes from zero-based indexing.

Assigning a value beyond the current end increases the length automatically. The indexes between the previous end and the new value become empty slots:

const cities = ['Lima', 'Oslo', 'Kyoto'];

cities[5] = 'Accra';

console.log(cities.length);
console.log(cities[5]);
console.log(3 in cities);
console.log(4 in cities);
6
Accra
false
false

Writing index 5 makes the length 6, because the range now runs from index 0 through index 5. Nothing was written at indexes 3 and 4.

That gives the first of three useful meanings for array size:

  • Index-range length is array.length, normally one greater than the highest index.
  • Occupied slots counts indexes where an element is present.
  • Matching values counts elements that satisfy a condition, such as scores of at least 10.

A dense array gives the same number for the first two meanings. A sparse array, which has one or more empty slots inside its length, does not.

Array length is not a fixed capacity. You can add an element beyond the current end, reduce length, or increase length later. The array’s index range changes with those operations.

This distinction also matters when counting in JavaScript: the correct counter follows the thing being counted, not the name you happen to give the result.

Length, Empty Slots, and Undefined Values

An array position can be in three distinct states:

  • An occupied slot with a value has an element such as 0, 'ready' or false.
  • An occupied slot containing undefined exists, and its stored value is undefined.
  • An empty slot, also called a hole, has no element at that index.

Use one sparse array to put all three states next to each other:

Index012345
Slotoccupiedholeoccupiedoccupiedholeoccupied
Value12undefined018
const readings = Array(6);

readings[0] = 12;
readings[2] = undefined;
readings[3] = 0;
readings[5] = 18;

console.log(readings.length);
console.log(1 in readings, readings[1]);
console.log(2 in readings, readings[2]);
console.log(3 in readings, readings[3]);
6
false undefined
true undefined
true 0

Index 1 is empty. Index 2 exists and contains undefined. Index 3 exists and contains the ordinary, falsy numeric value 0.

The expression index in array tests whether a property exists at that index. Reading indexes 1 and 2 produces undefined in both cases, but the in checks reveal the difference. The first index is missing; the second is present.

Same read, different existenceindex 1no elementindex 2stores undefinedreadreadreads undefinedreads undefined1 in array: false2 in array: true
The same read result can hide two different slot states.

That difference changes the counts:

const readings = Array(6);

readings[0] = 12;
readings[2] = undefined;
readings[3] = 0;
readings[5] = 18;

const occupiedSlots = readings.filter(() => true).length;
const definedValues = readings.filter(
  value => value !== undefined
).length;

console.log(readings.length);
console.log(occupiedSlots);
console.log(definedValues);
3
2
1

The index-range length is 3. There are two occupied slots, because indexes 1 and 2 are present. There is one defined value, because the explicit undefined at index 1 does not pass the second filter.

filter() does not call its predicate for empty slots. That is why filter(() => true) keeps every present element, including the explicit undefined, while skipping the hole.

Increasing length creates the same kind of empty slot. It does not write undefined into each new position:

const readings = [14];

readings.length = 3;

console.log(readings.length);
console.log(1 in readings);
console.log(2 in readings);
3
false
false

The array now covers three indexes, but only index 0 is occupied. This is why indexed access alone cannot tell a hole from an explicit undefined.

The distinction fits JavaScript’s broader data types: undefined is a value. A hole is the absence of an indexed element.

Count the Thing You Actually Mean

Start by naming the count. The same array can have a length of 6, four occupied slots and different matching-value counts: three for value !== undefined, or two for the passing rule.

The same readings array contains all three slot states and both example rules:

const readings = Array(6);

readings[0] = 12;
readings[2] = undefined;
readings[3] = 0;
readings[5] = 18;

const indexRange = readings.length;
const occupiedSlots = readings.filter(() => true).length;
const valuesNotUndefined = readings.filter(
  value => value !== undefined
).length;
const passingValues = readings.filter(
  value => typeof value === 'number' && value >= 10
).length;

console.log(indexRange);
console.log(occupiedSlots);
console.log(valuesNotUndefined);
console.log(passingValues);
6
4
3
2

Indexes 1 and 4 are holes. Index 2 is occupied but contains undefined. Index 3 contains 0, which is a defined number but does not meet the passing rule.

One array, three counts12undef018length: 6present?12undef018occupied: 4number ≥ 10?1218passing: 2
Different questions select different parts of the same array.

The recipe follows the question:

What “size” meansRecipeResult for readings
Index rangereadings.length6
Occupied slotsreadings.filter(() => true).length4
Values matching the passing rulereadings.filter(value => typeof value === 'number' && value >= 10).length2

The last recipe is the one to use for questions such as “How many tasks are complete?” or “How many prices are below 20?” Its callback states the rule, and .length counts the filtered result. Another rule, such as value => value !== undefined, produces a different matching-value count.

filter(Boolean) is not a general element counter. It removes every value that converts to false, including valid values such as 0, false, NaN, null and an empty string:

const responses = [0, false, '', null, NaN, 'saved'];

console.log(responses.length);
console.log(responses.filter(Boolean).length);
console.log(
  responses.filter(value => value !== undefined).length
);
6
1
6

All six slots are occupied, and none contains undefined. filter(Boolean) returns only 'saved', so it answers “How many values are truthy?” That is a different question.

All 6 slots are occupied0falsenullNaNsavedBoolean(value)5 falsy values stoptruthy: 1
A truthiness filter rejects several valid occupied values.

A loop is useful when you need the count without creating a filtered array, or when counting is one part of a larger pass through the data. Loops: while and for develops that pattern from the first iteration onward.

Create an Array of a Given Size

Array(n) creates an array with a length of n and n empty slots. It does not create n elements containing undefined:

const reserved = Array(3);

console.log(reserved.length);
console.log(0 in reserved);
console.log(1 in reserved);
console.log(2 in reserved);
3
false
false
false

Use Array(n) when an empty index range is genuinely what you need. If later code expects initialized values, choose a creation method that writes them.

fill(value) places a value into every slot:

const flags = Array(3).fill(false);

console.log(flags.length);
console.log(0 in flags);
console.log(flags.join(', '));
3
true
false, false, false

All three positions now exist and contain false. Repeated primitive values such as numbers, strings and booleans work cleanly with fill().

Same length, different contentsArray(3)Array(3).fill(false)holeholeholefalsefalsefalselength 3length 30 occupied3 occupied
The same length does not guarantee the same occupied slots.

Objects need more care. fill({}) repeats one object reference rather than creating a separate object for each index:

const rows = Array(3).fill({ status: 'waiting' });

rows[0].status = 'done';

console.log(rows[0].status);
console.log(rows[1].status);
console.log(rows[2].status);
console.log(rows[0] === rows[1]);
done
done
done
true

Changing the object through rows[0] is visible through every slot because all three point to the same object.

Use Array.from() with a mapping function when each position needs its own value:

const rows = Array.from(
  { length: 3 },
  (_, index) => ({
    id: index + 1,
    status: 'waiting',
  })
);

rows[0].status = 'done';

console.log(rows[0].status);
console.log(rows[1].status);
console.log(rows[2].status);
console.log(rows[0] === rows[1]);
done
waiting
waiting
false

The mapping function runs for every index. Each call returns a new object, so changing the first row leaves the other two alone.

fill({}) shares one objectslot 0slot 1slot 2one sharedobjectArray.from() creates threeslot 0slot 1slot 2object Aobject Bobject C
Shared and separate object references behave differently.

The same form can create numbers derived from their indexes:

const pageNumbers = Array.from(
  { length: 5 },
  (_, index) => index + 1
);

console.log(pageNumbers.join(', '));
1, 2, 3, 4, 5

Unlike Array(5), this result has five occupied slots. The values are initialized as the array is created.

The choice is compact:

  • Use Array(n) for an index range containing empty slots.
  • Use Array(n).fill(primitive) for one repeated primitive value.
  • Use Array.from({ length: n }, mapper) for per-index values or separate objects.

These creation patterns sit next to array transformations and other core language tools in JavaScript Fundamentals.

Changing Length and Avoiding Common Mistakes

Assigning a smaller number to length truncates the array. Elements at indexes outside the new range are deleted:

const queue = ['draft', 'review', 'publish', 'archive'];

queue.length = 2;

console.log(queue.length);
console.log(queue.join(', '));
console.log(2 in queue);
2
draft, review
false

The array keeps indexes 0 and 1. The values at indexes 2 and 3 are gone.

Assigning 0 clears every indexed element:

const queue = ['draft', 'review'];

queue.length = 0;

console.log(queue.length);
console.log(queue[0]);
0
undefined

Increasing length does the opposite to the range, but it does not restore deleted values or initialize new ones. It creates empty slots.

length 4draftreviewpublisharchiveset length = 2length 2draftreviewdeleteddeletedset length = 4length 4draftreviewholeholepublish and archive do notreturn
Shrinking deletes values; growing later creates empty positions.

A valid array length is a nonnegative integer below 2 ** 32. The language-level maximum is 4,294,967,295, as documented in MDN’s Array: length reference, but that number is not a promise that a machine can hold a useful array of that size. Available memory and runtime limits can be lower.

Negative numbers, fractions and 2 ** 32 are invalid lengths:

for (const length of [-1, 1.5, 2 ** 32]) {
  try {
    Array(length);
  } catch (error) {
    console.log(error.name);
  }
}
RangeError
RangeError
RangeError

Finally, keep collection terminology separate. Arrays use .length, while Set and Map use .size:

const tags = ['js', 'arrays', 'js'];
const uniqueTags = new Set(tags);
const visits = new Map([
  ['home', 12],
  ['docs', 8],
]);

console.log(tags.length);
console.log(uniqueTags.size);
console.log(visits.size);
3
2
2

The array has three positions. The Set has two elements after removing the repeated 'js' value, and the Map has two entries. Same word in conversation, different properties in code.

What does each collection count?Arrayjsarraysjslength 3Setjsarraysduplicate js mergessize 2Maphome → 12docs → 8size 2two key–value entries
Arrays count positions with length; Sets and Maps count contents with size.

Frequently asked questions

How do I get the size of an array in JavaScript?
Read the array's length property with array.length. Arrays do not have a standard size property or size() method.
Why can array.length be larger than the number of elements?
Length describes the array's index range, not always the number of occupied slots. Empty slots still sit inside that range, even though no element exists at those indexes.
How do I count non-empty array slots?
Use array.filter(() => true).length when you need to count present indexes while skipping empty slots. This count includes an occupied slot whose value is undefined.
How do I create an array with a specific size?
Array(n) creates a length of n with empty slots. Use Array(n).fill(value) for a repeated value, or Array.from({ length: n }, mapper) when each index needs its own initialized value.
Do JavaScript arrays use size or length?
Arrays use the length property. Set and Map collections use size instead, so new Set(values).size is valid while values.size is undefined when values is an array.