Дан массив:

const arr = [1,2,3,4,5]

arr.push(0) повлияет на массив так же, как если бы мы выполнили:

Теория: массив, индексы, length

С точки зрения языка массив — это объект со свойствами, где “числовые индексы” являются особыми именами свойств, а length автоматически согласуется с наибольшим индексом.
Значение length — неотрицательное целое, и оно всегда численно больше наибольшего индекса, который реально присутствует в массиве.

length изменяется не “по количеству присвоений”, а по правилу: если установлено значение по индексу, который больше либо равен текущему length, то length увеличивается, чтобы быть больше этого индекса.

Схема для const arr = [1,2,3,4,5] (до изменений):

индекс:  0  1  2  3  4
знач.:   1  2  3  4  5
length:  5

Разбор каждого варианта

Сравнение удобно делать по двум критериям: какой элемент меняется и меняется ли length.
Для исходного массива arr.length равно 5, значит “конец массива” в терминах индекса — это позиция 5 (следующая после 4).

Таблица эффектов (исходно arr = [1,2,3,4,5], length = 5):

ОперацияЧто меняетсяНовый массив (видимая часть)Новый length
arr.push(0)Добавляется элемент по следующему индексу в конце[1,2,3,4,5,0]6
arr[0] = 0Перезаписывается первый элемент[0,2,3,4,5]5
arr[arr.length] = 0Создаётся элемент по индексу 5 (так как length был 5)[1,2,3,4,5,0]6
arr[arr.length - 1] = 0Перезаписывается индекс 4[1,2,3,4,0]5
arr[-1] = 0Создаётся свойство с ключом "-1" (не индекс массива)[1,2,3,4,5] и доп. свойство5

Почему вариант 2 эквивалентен:

  • При arr[arr.length] = 0 сначала вычисляется текущее arr.length (это 5), затем выполняется присваивание arr[5] = 0, и так как индекс 5 находится “за концом”, length становится 6.
  • push делает именно добавление в конец и обновляет length (плюс возвращает новую длину как значение выражения).

Почему вариант 4 не эквивалентен:

  • “Отрицательный индекс” в квадратных скобках на практике становится доступом к свойству объекта по имени "-1", то есть это не элемент массива и не влияет на length.
Эквивалентность arr.push(x) и arr[arr.length] = x относится к эффекту на содержимое и length, но не к возвращаемому значению: push возвращает новое значение length, а оператор присваивания возвращает присвоенное значение (например, 0).

Примеры и проверки

Ниже показаны проверки, которые позволяют увидеть отличия в результате и в length.

Пример 1: сравнение push и arr[arr.length]

const a = [1,2,3,4,5];
const b = [1,2,3,4,5];

const r1 = a.push(0);
const r2 = (b[b.length] = 0);

console.log(a, a.length, r1);
console.log(b, b.length, r2);

Ожидаемая логика:

  • a и b станут [1,2,3,4,5,0].
  • a.length и b.length станут 6.
  • r1 будет 6, а r2 будет 0.

Пример 2: почему arr[-1] “не последний элемент”

const arr = [1,2,3,4,5];
arr[-1] = 0;

console.log(arr.length);     // 5
console.log(arr[-1]);        // 0
console.log(arr[4]);         // 5

Объяснение наблюдения: значение хранится в свойстве "-1", а не в “последнем элементе массива”.

Пример 3: безопасный доступ к последнему элементу

const arr = [1,2,3,4,5];
const last = arr[arr.length - 1];

console.log(last); // 5

Кратко: эквивалентное воздействие на массив (добавление 0 в конец и увеличение length) даёт только arr[arr.length] = 0;, тогда как arr[0] = 0; и arr[arr.length - 1] = 0; перезаписывают существующие элементы, а arr[-1] = 0; создаёт обычное свойство "-1", не меняя length.