TGStat
TGStat
Введите текст для поиска
Расширенный поиск каналов
  • Язык сайта
    flag Russian flag English flag Uzbek
  • Вход на сайт
  • Каталог
    Каталог каналов и чатов Региональные подборки Тематические подборки Платные каналы Поиск каналов
    Добавить канал/чат
  • Рейтинги
    Рейтинг каналов Рейтинг чатов Рейтинг публикаций
    Рейтинги брендов и персон
  • Аналитика
  • Поиск по публикациям
  • Мониторинг Telegram
  • Продвижение
    Реклама через Яндекс Бизнес Реклама в каналах через TGStat Agency Реклама на сайте TGStat.ru
JavaScript Ready | Программирование

24 Sep, 11:12

Открыть в Telegram Поделиться Пожаловаться

Почему Array.sort() может создать баг далеко от места сортировки!

С sort() проблема не в самой сортировке, а в скрытой мутации. Вызов выглядит как обычное получение отсортированного результата, хотя одновременно изменяет сам исходный массив:
const scores = [30, 10, 20];

const sorted = scores.sort(
(a, b) => a - b
);

console.log(scores);
// [10, 20, 30]

sort() сортирует массив непосредственно и возвращает ту же ссылку. Поэтому изменение увидит не только код, работающий с sorted, но и любой код, который уже хранит ссылку на исходный массив:
const scores = [30, 10, 20];
const cached = scores;

scores.sort((a, b) => a - b);

console.log(cached);
// [10, 20, 30]

Особенно неприятно это становится на границе функций. По названию getSortedUsers() можно ожидать получение нового результата, но реализация незаметно меняет массив вызывающего кода:
function getSortedUsers(users) {
return users.sort(
(a, b) => a.age - b.age
);
}

В результате функция имеет побочный эффект, который не очевиден из места её вызова. Это уже может затронуть состояние, кэш, мемоизацию или другой код, использующий тот же массив:
const users = getUsers();
const original = users;

getSortedUsers(users);

console.log(original === users);
// true

Классический способ избежать этого — сначала создать новый массив и сортировать уже его. Такой код работает, но копирование приходится выражать отдельной операцией:
const sorted = [...scores].sort(
(a, b) => a - b
);

console.log(scores);
// [30, 10, 20]

toSorted() делает намерение явным: получить новый массив с другим порядком элементов, не изменяя исходный. При этом возвращается уже другая ссылка:
const sorted = scores.toSorted(
(a, b) => a - b
);

console.log(scores);
// [30, 10, 20]

console.log(scores === sorted);
// false

Важно, что копируется именно массив. Объекты внутри него не клонируются, поэтому обе коллекции по-прежнему содержат ссылки на одни и те же объекты:
const users = [
{ name: "Alex", age: 30 },
{ name: "Bob", age: 20 },
];

const sorted = users.toSorted(
(a, b) => a.age - b.age
);

console.log(
sorted[0] === users[1]
);
// true

Поэтому изменение самого объекта будет видно через обе коллекции:
sorted[0].age = 25;

console.log(users[1].age);
// 25

Похожая логика есть у toReversed() и toSpliced(): они позволяют выполнить соответствующую операцию без изменения исходного массива, в отличие от мутирующих reverse() и splice().

🔥 Поэтому выбор между sort() и toSorted() — это не просто вопрос синтаксиса, это выбор контракта операции: изменить существующий массив или получить новый, сохранив исходный. Чем больше у массива потребителей, тем важнее эта разница.

📣 JS Ready | #практика

1k 0 12 20
Каталог
Каталог каналов и чатов Подборки каналов Поиск каналов Добавить канал/чат
Рейтинги
Рейтинг каналов Telegram Рейтинг чатов Telegram Рейтинг публикаций Рейтинги брендов и персон
API
API статистики API поиска публикаций API Callback
Наши каналы
@TGStat @TGStat_Chat @telepulse @TGStatAPI
Почитать
Академия TGStat Исследование Telegram 2019 Исследование Telegram 2021 Исследование Telegram 2023
Контакты
Справочный центр Поддержка Почта Вакансии
Всякая всячина
Пользовательское соглашение Политика конфиденциальности Публичная оферта
Наши боты
@TGStat_Bot @SearcheeBot @TGAlertsBot @tg_analytics_bot @TGStatChatBot