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() изменяет конкретный массив, на который ссылается полученный объект. Он не трогает другие массивы и не обновляет интерфейс автоматически. Как только это различие становится очевидным, причина находится быстро.
Комментарии
Чтобы оставить комментарий, войдите в аккаунт.