JavaScript: почему push() не меняет массив

mr. Cooper • 2 недели назад • Веб-разработка
JavaScript: почему push() не меняет массив

push() - один из самых предсказуемых методов в JavaScript. Он добавляет элемент в конец массива, возвращает новую длину и может выбросить исключение, если массив нельзя изменить. Поэтому баг «элемент не добавился» почти всегда означает не поломку метода, а то, что изменился не тот массив, который вы потом проверяете.

Разберём, где именно теряется элемент.

Базовый случай

//js

const items = [];

items.push('apple');

console.log(items);        // ['apple']
console.log(items.length); // 1

Здесь всё работает. Значит, если в реальном коде длина остаётся прежней, искать причину нужно не в push(), а в том, на какой массив он в этот момент ссылается.

push() изменяет исходный массив. Он не создаёт новый и не трогает переменную, из которой массив был получен.

Ссылка и параметр функции

Это различие становится заметным, когда массив передаётся в функцию.

//js

const items = ['apple'];

function addItem(list) {
list.push('orange');
}

addItem(items);

console.log(items); // ['apple', 'orange']

Функция получает значение, ссылающееся на тот же массив. Изменение видно снаружи.

Другое дело - если внутри функции параметру присвоить новый массив:

//js

const items = ['apple'];

function addItem(list) {
list = [];
list.push('orange');
}

addItem(items);

console.log(items); // ['apple']

push() сработал. Но добавил элемент в новый массив, на который теперь ссылается list. Переменная items осталась при своём.

Вывод: проблема не в добавлении элемента, а в том, что код изменяет не тот объект, который вы проверяете.

Переприсваивание свойства объекта

Похожая ловушка - массив внутри объекта.

//js

const state = {
items: ['apple']
};

const previousItems = state.items;

state.items = [];
state.items.push('orange');

console.log(state.items);    // ['orange']
console.log(previousItems);  // ['apple']

Строка state.items = [] создаёт новый массив. previousItems продолжает ссылаться на старый. Присваивание значения свойству объекта не обновляет ссылки, сохранённые в других переменных.

При отладке стоит смотреть не только на имя переменной, но и на то, откуда она получила массив и не было ли после этого переприсваивания.

Код, который не выполняется

Иногда до push() дело просто не доходит.

//js

if (shouldAdd) {
items.push('apple');
}

При shouldAdd === false вызова не будет. Ошибки тоже не будет - условие молча пропустит блок.

Прежде чем менять метод или переписывать массив, проверьте сам момент вызова:

//js

console.log('до:', items.length);

const result = items.push('apple');

console.log('после:', items.length);
console.log('push вернул:', result);

Если до вызова длина была 0, после добавления одного элемента она станет 1. Метод вернёт то же число.

Если результат совпадает с ожиданиями, push() сработал. Дальше ищите, какой массив используется по коду и не заменяется ли он между вызовом и чтением данных.

Отдельный случай: React

В React изменение массива через push() не сообщает React, что состояние нужно обновить.

//jsx

const [items, setItems] = useState([]);

function addItem() {
items.push('apple');
console.log(items.length); // 1
}

Массив действительно изменится, items.length станет равен 1. Но компонент не перерисуется. Если он не обновится по другой причине, интерфейс останется прежним.

Обычное решение - создать новый массив:

//jsx

function addItem() {
setItems(current => [...current, 'apple']);
}

Здесь формируется новый массив из предыдущего состояния и добавленного 'apple', и уже он передаётся в setItems. React видит новую ссылку и обрабатывает обновление.

push() при этом ни сломан, ни запрещён. Просто изменение массива и обновление состояния интерфейса - разные операции.

Что проверять по порядку

Изменился ли массив сразу после push().

Если изменился - не используется ли дальше другая ссылка: параметр, переприсвоенное свойство, новый массив.

Если длина прежняя - выполнилась ли нужная строка и что реально передано в push(). Например, items.push(...source) при пустом source передаст ноль аргументов, и длина не изменится.

Если это React - не ждёте ли вы обновления интерфейса от мутации, которая его не запускает.

push() изменяет конкретный массив, на который ссылается полученный объект. Он не трогает другие массивы и не обновляет интерфейс автоматически. Как только это различие становится очевидным, причина находится быстро.

Комментарии

Пока нет комментариев. Будьте первым, кто напишет.

Чтобы оставить комментарий, войдите в аккаунт.

Похожие статьи