<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[findIndex против for + break: поиск товара по ID без полного скана каталога и багов с -1]]></title><description><![CDATA[<p dir="auto">Каждый день фронтендеры роются в каталогах товаров - массив из тысяч объектов. Ищут по ID, а код тормозит на больших данных. Разберём findIndex против классического for с break: где скорость, где читаемость, и как не словить -1 в неожиданном месте.</p>
<p dir="auto">Это не теория - практика из реальных приложений. Поможет выбрать инструмент под задачу, избежать утечек производительности и типичных косяков с возвращаемыми значениями. Заодно разберём, почему новички любят forEach, а потом удивляются лагам.</p>
<h2>Почему findIndex бьёт forEach и filter на больших каталогах</h2>
<p dir="auto">Методы массива типа findIndex и find останавливаются на первом совпадении. Никаких лишних итераций - нашли товар по ID, вернули индекс и вышли. А forEach или filter пробегают весь каталог, даже если нужный объект на нулевом месте. Это классическая <strong>утечка циклов</strong>, когда код выглядит функционально, но жрёт CPU зря.</p>
<p dir="auto">Представь каталог на 10k товаров. findIndex с предикатом <code>item =&gt; item.id === targetId</code> вернёт 42 или -1, не трогая хвост массива. for + break делает то же самое вручную, но под полным контролем. Тесты показывают: for выигрывает на миллионах элементов, findIndex близко следует за ним. А filter? Создаст новый массив со всеми товарами - память в трубу.</p>
<ul>
<li><strong>Преимущества findIndex</strong>: читаемый код, встроенная логика выхода, работает с any[], не требует ручного индекса.</li>
<li><strong>Когда for с break</strong>: критичные hot paths, где каждая микросекунда на счету, или legacy-код без ES6.</li>
<li><strong>Антипаттерн forEach</strong>: всегда до конца, даже после console.log(‘найден!’). Не для поиска.</li>
</ul>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Метод</th>
<th>Возврат</th>
<th>Остановка?</th>
<th>Скорость на 100k элементов</th>
</tr>
</thead>
<tbody>
<tr>
<td>findIndex</td>
<td>индекс или -1</td>
<td>да, на первом</td>
<td>высокая</td>
</tr>
<tr>
<td>for + break</td>
<td>индекс</td>
<td>да, на break</td>
<td>максимальная</td>
</tr>
<tr>
<td>forEach</td>
<td>undefined</td>
<td>нет</td>
<td>низкая для поиска</td>
</tr>
<tr>
<td>filter</td>
<td>новый массив</td>
<td>нет</td>
<td>медленная, жрёт память</td>
</tr>
</tbody>
</table>
<h2>Типичные баги с -1 и как их словить заранее</h2>
<p dir="auto">findIndex вернёт -1, если ничего не нашёл. Легко забыть проверку: код думает, что индекс  -1 валиден, и падает на array[-1]. Или путают с find(), который кидает undefined - там свой set багов. А в for-цикле ты сам контролируешь: if (found) break, else return -1.</p>
<p dir="auto">Реальный пример: корзина товаров, ищем по ID для апдейта. findIndex даёт 5, но типизация слабая - вдруг id не number? Строгое === спасает от ‘123’ == 123. В for можно добавить тип-чек внутри цикла. Ещё засада: мутирующий массив во время поиска. findIndex клонирует логику, но не защищает от splice в колбэке.</p>
<ul>
<li><strong>Баг #1</strong>: <code>if (idx = array.findIndex(...))</code> - присвоение вместо сравнения, всегда truthy.</li>
<li><strong>Баг #2</strong>: Нет проверки <code>if (idx !== -1)</code> перед array[idx] - обращение к несуществующему элементу.<br />
<em>В TypeScript: укажи тип возврата <code>number | -1</code>, ESLint подскажет.</em></li>
<li><strong>Баг #3</strong>: Предикат с побочками - <code>item.id === id &amp;&amp; item.price *= 0.9</code> мутирует данные.</li>
</ul>
<pre><code class="language-javascript">const idx = catalog.findIndex(item =&gt; item.id === targetId &amp;&amp; item.id &gt; 0);
if (idx &gt; -1) {
  // safe update
} else {
  console.warn('Товар не найден');
}
</code></pre>
<h2>For + break под капотом: микро-оптимизация без фреймворков</h2>
<p dir="auto">for (let i = 0; i &lt; catalog.length; i++) { if (catalog[i].id === targetId) return i; } return -1; Простой, как велосипед. Нет overhead от Function конструктора, нет замыканий, чистый ивент-луп без вмешательств. findIndex под капотом примерно то же - C++ цикл в V8 с early exit.</p>
<p dir="auto">Разница в бандле: findIndex - 0 байт, чистый JS. for такой же. Но в минифицированном коде for короче на символы. На мобильных каталогах 50k+ for выигрывает 10-20%. Ещё плюс: легко добавить условия - <em>проверить stock &gt; 0</em>, без переписывания предиката.</p>
<ul>
<li><strong>Микро-версия findIndex</strong>:</li>
</ul>
<pre><code class="language-javascript">function myFindIndex(arr, pred) {
  for (let i = 0; i &lt; arr.length; i++) {
    if (pred(arr[i], i, arr)) return i;
  }
  return -1;
}
</code></pre>
<ul>
<li>Тестируй на реальных данных: Array.from({length: 1e5}, (_, i) =&gt; ({id: i}));</li>
<li><strong>Профиль</strong>: Chrome DevTools покажет, где тормозит - в колбэке или цикле.</li>
</ul>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Сценарий</th>
<th>findIndex</th>
<th>for + break</th>
<th>Рекомендация</th>
</tr>
</thead>
<tbody>
<tr>
<td>Малый массив (&lt;1k)</td>
<td><img src="https://forum.exlends.com/assets/plugins/nodebb-plugin-emoji/emoji/android/2705.png?v=a9b928d4b2f" class="not-responsive emoji emoji-android emoji--white_check_mark" style="height:23px;width:auto;vertical-align:middle" title="✅" alt="✅" /> читаемо</td>
<td><img src="https://forum.exlends.com/assets/plugins/nodebb-plugin-emoji/emoji/android/2705.png?v=a9b928d4b2f" class="not-responsive emoji emoji-android emoji--white_check_mark" style="height:23px;width:auto;vertical-align:middle" title="✅" alt="✅" /> просто</td>
<td>findIndex</td>
</tr>
<tr>
<td>Большой каталог</td>
<td>близко к for</td>
<td><img src="https://forum.exlends.com/assets/plugins/nodebb-plugin-emoji/emoji/android/2705.png?v=a9b928d4b2f" class="not-responsive emoji emoji-android emoji--white_check_mark" style="height:23px;width:auto;vertical-align:middle" title="✅" alt="✅" /> быстрее</td>
<td>for</td>
</tr>
<tr>
<td>С мутациями</td>
<td>риск</td>
<td>контроль</td>
<td>for</td>
</tr>
</tbody>
</table>
<h2>findIndex в связке с другими методами: не всё так просто</h2>
<p dir="auto">Часто ищут не только индекс. Хочешь удалить товар? findIndex + splice. Обновить? findIndex + mutate. Но splice на больших массивах - O(n) сдвиг, лучше filter для immutable. Или Map по ID - O(1) поиск, но жрёт память.</p>
<p dir="auto">Ещё нюанс: sparse arrays или удалённые элементы (delete arr). findIndex их пропустит? Нет, перебирает индексы последовательно. В for то же. Но с Map/Set никаких дыр. Типичный косяк: поиск по строковому ID с Number(id), когда id - number.</p>
<ul>
<li><strong>Комбо #1</strong>: <code>splice(items.findIndex(i =&gt; i.id === id), 1)</code> - удаление по ID.<br />
<em>Immutable: <code>filter(item =&gt; item.id !== id)</code> - новый массив.</em></li>
<li><strong>Комбо #2</strong>: <code>const idx = findIndex(...); items[idx]?.price += delta;</code> - optional chaining спасает от -1.</li>
<li><strong>Когда Map лучше</strong>: частые lookup’ы, &gt;10k элементов.</li>
</ul>
<h2>Бей по костылям: выбирай цикл под задачу</h2>
<p dir="auto">В итоге findIndex - для читаемого кода в 80% случаев. for + break - когда секунды тикают в large-scale apps. Главное - всегда проверяй на -1, пиши предикаты без side-effects и профилируй на реальных данных.</p>
<p dir="auto">Осталось за кадром: weak maps для GC-safe кэша ID, или как V8 оптимизирует inline-колбэки. Подумать стоит над тем, чтобы вынести поиск в worker для mega-каталогов - main thread вздохнёт свободнее.</p>
]]></description><link>https://forum.exlends.com/topic/2062/findindex-protiv-for-break-poisk-tovara-po-id-bez-polnogo-skana-kataloga-i-bagov-s-1</link><generator>RSS for Node</generator><lastBuildDate>Tue, 14 Apr 2026 23:50:36 GMT</lastBuildDate><atom:link href="https://forum.exlends.com/topic/2062.rss" rel="self" type="application/rss+xml"/><pubDate>Tue, 14 Apr 2026 13:36:58 GMT</pubDate><ttl>60</ttl></channel></rss>