Масив, който расте
Проблем
Количката е пълна
Пишеш количката на онлайн магазин и пазиш продуктите в масив, както в урока за масивите: всеки продукт на своя индекс. JavaScript позволява да пишеш и след края, но кой е първият свободен индекс, следиш ти. А когато клиентът махне продукт, остава дупка:
const cart = ["Хляб", "Мляко", "Кафе"];
// Четвърти продукт: пишем на първия свободен индекс.cart[cart.length] = "Масло";
// Клиентът маха млякото.cart[1] = undefined;
for (const item of cart) { console.log(item);}console.log(cart.length);ХлябundefinedКафеМасло4undefined. А броят продукти вече не е cart.length: той е 4, а продуктите са 3.Това е много код за нещо, което всеки магазин прави постоянно.
Идеята
push и splice: масивът се грижи сам
Масивът има методи, които правят това вместо теб. Копирането и местенето прави сам. Колекция, която расте и се свива така, се казва списък, а в JavaScript това е самият масив:
const cart = [];cart.push("Хляб");cart.push("Мляко");cart.push("Кафе");cart.push("Масло");cart.splice(cart.indexOf("Мляко"), 1);console.log(cart);console.log(`Продукти: ${cart.length}`);[ 'Хляб', 'Кафе', 'Масло' ]Продукти: 3pushдобавя в края.- Метод, който маха по стойност, няма.
indexOfнамира индекса на „Мляко“, аsplice(индекс, 1)маха един елемент оттам и затваря дупката: „Кафе“ се мести на мястото на „Мляко“. lengthе колко елемента има в момента.console.logпечата целия масив в квадратни скоби, а текста в кавички.
Ето какво прави масивът вътре. Колко места има, не избираш ти, а двигателят, който изпълнява кода (V8 в Node и Chrome). Масив от 3 продукта в кода има точно 3 места, затова се пълни на четвъртия:
const cart = ["Хляб", "Мляко", "Кафе"];cart.push("Масло");cart.splice(cart.indexOf("Мляко"), 1);console.log(cart);console.log(`Продукти: ${cart.length}`);Стъпка по стъпка, с думи
- cart сочи масив. Отвътре той има length и отделен масив elements с 3 места.
- Продуктите от кода заемат местата един по един. Масивът е пълен: 3 от 3.
- Няма място. push прави масив с 22 места и копира елементите в него.
- elements вече сочи новия масив, а старият не трябва на никого. „Масло“ влиза на свободното място.
- splice маха „Мляко“, а всичко след него се мести с едно място наляво. Дупка не остава.
- Отвън се вижда само масив с три продукта. Колко места има отвътре, решава двигателят V8: езикът не го обещава.
По-точно, за любопитните
- Има и
delete cart[1], но то не мести нищо. Оставя дупка, аlengthостава същият:
const cart = ["Хляб", "Мляко", "Кафе"];delete cart[1];console.log(cart, cart.length);[ 'Хляб', <1 empty item>, 'Кафе' ] 3За масив ползвай splice.
Идеята
Какво има в масива
Масивът не пита какво слагаш в него. Текст и число могат да стоят едно до друго:
const cart = [];cart.push("Хляб");cart.push(5);console.log(cart);[ 'Хляб', 5 ]Това изглежда удобно, но грешката излиза по-късно, там, където очакваш текст. Затова един масив пази един вид неща: само продукти, само цени.
В самия JavaScript няма как да запишеш „масив само от текст“. Това прави TypeScript, JavaScript с типове: там масивът от текст е string[] и cart.push(5) е грешка още в редактора.
Идеята
Индекси: [i], splice, indexOf, includes
Масивът има индекси от 0 до tasks.length - 1. Четеш и заменяш с [ ], а вмъкваш със splice:
const tasks = ["Фактура за март", "Обаждане до банката", "Отчет за склада"];
console.log(tasks[0]);tasks[1] = "Среща в банката";tasks.splice(0, 0, "Кафе с екипа");console.log(tasks.join(", "));console.log(tasks.indexOf("Отчет за склада"));console.log(tasks.includes("Обаждане до банката"));Фактура за мартКафе с екипа, Фактура за март, Среща в банката, Отчет за склада3falsetasks[i]чете,tasks[i] = xзаменя.splice(i, 0, x)вмъква на индексiи мести останалите надясно. Нулата значи „не махай нищо“.indexOf(x)търси и връща индекса или-1, аincludes(x)казва само дали го има.join(", ")слепва елементите в един текст, разделени със запетая.
По-точно, за любопитните
- Индекс извън масива не спира програмата, а дава
undefined.at(-1)брои отзад и дава последния елемент:
const tasks = ["Фактура за март", "Обаждане до банката", "Отчет за склада"];console.log(tasks[3]);console.log(tasks.at(-1));console.log(tasks.indexOf("Заплати"));undefinedОтчет за склада-1includesиindexOfсравняват с===. Текстът е стойност, затова намират текст, който е равен.- Вмъкването и махането отпред са бавни при голям масив: всички елементи след индекса се местят с едно място. За един милион елемента това е един милион премествания.
Пробвай
Кой е на ред?
Списъкът с чакащите при лекаря. Иван влиза, а Ана има час и се нарежда втора.
Какво ще отпечата програмата?
const waiting = ["Иван", "Мария", "Петър"];waiting.shift();waiting.splice(1, 0, "Ана");console.log(waiting, waiting.length);shift() маха Иван и Мария става първа, на индекс 0. splice(1, 0, "Ана") вмъква Ана на индекс 1 и бута Петър надясно. Остават трима.
shift() маха първия елемент, както splice(0, 1).
Идеята
Обхождане
Масивът се обхожда с for...of, когато ти трябват само елементите, или с индекс, когато ти трябва и номерът:
const cart = ["Хляб", "Кафе", "Масло"];
for (const item of cart) { console.log(`- ${item}`);}for (let i = 0; i < cart.length; i++) { console.log(`${i + 1}. ${cart[i]}`);}- Хляб- Кафе- Масло1. Хляб2. Кафе3. МаслоПо-точно, за любопитните
- Не махай елементи от масива, докато го обхождаш с
for...of. Програмата не спира, но прескача елементи:
const cart = ["Хляб", "Хляб", "Мляко"];for (const item of cart) { if (item === "Хляб") { cart.splice(cart.indexOf(item), 1); }}console.log(cart);[ 'Хляб', 'Мляко' ]Вторият „Хляб“ остана. splice премести всичко наляво, а цикълът продължи от следващия индекс и не го видя. Грешка няма, затова такъв пропуск се хваща трудно.
- Ако трябва да махаш, обхождай с индекс отзад напред. Така махането не мести елементите, които още не си проверил:
const cart = ["Хляб", "Хляб", "Мляко"];for (let i = cart.length - 1; i >= 0; i--) { if (cart[i] === "Хляб") { cart.splice(i, 1); }}console.log(cart);[ 'Мляко' ]filter.Идеята
Числа в масив
Масив от оценки е като масив от текст. Числото стои в масива, както си е:
const scores = [85, 64, 91];
let total = 0;for (const score of scores) { total += score;}console.log(`Средно: ${total / scores.length}`);Средно: 80JavaScript печата 80, а не 80.0: за него цялото и дробното число са един и същ тип, number.
По-точно, за любопитните
- Числото в JavaScript е проста стойност, не обект. Затова
===сравнява стойностите и две равни числа са равни. Има и обвивкаnew Number(257), която прави обект, а два обекта са различни, дори да пазят едно и също:
const c = Number("257");const d = Number("257");console.log(c === d);console.log(new Number(257) === new Number(257));truefalsenew Number не ти трябва. Пиши числата направо.
Пробвай
Намери грешката
Залата пази заетите места в масив. Клиентът се отказва от място 2 и програмата трябва да отпечата [ 12, 7, 5 ]. Вместо това печата [ 12, 2, 5 ]. На кой ред е грешката?
Избери реда с грешката.
splice(2, 1) маха един елемент от индекс 2, тоест мястото 7. За да махнеш стойността 2, първо намери индекса ѝ: seats.splice(seats.indexOf(2), 1). Внимавай, ако стойността я няма: indexOf дава -1, а splice(-1, 1) маха последния елемент. Затова първо провери с includes.По-точно, за любопитните
const seats = [12, 7, 5];seats.splice(seats.indexOf(2), 1);console.log(seats);[ 12, 7 ]Идеята
Подреждане и масив, който не се променя
sort() подрежда масива на място. Текстът излиза по азбучен ред. Числата обаче не излизат по големина:
const cities = ["София", "Пловдив", "Варна"];cities.sort();console.log(cities);
const prices = [30, 5, 12];prices.sort();console.log(prices);prices.sort(function (a, b) { return a - b;});console.log(prices);[ 'Варна', 'Пловдив', 'София' ][ 12, 30, 5 ][ 5, 12, 30 ]Без правило sort превръща всичко в текст и сравнява текста. „12“ е преди „30“, а „30“ преди „5“, както в речника. Затова на числата даваш правило: функция, която получава две числа и връща a - b. Отрицателен резултат значи „a е преди b“. Засега я копирай, а в урока „Класове в класове“ ще видиш как се пишат такива правила.
Масив, който не се променя, се прави с Object.freeze. Той е неизменим: не можеш да добавяш, махаш или подреждаш в него.
const days = Object.freeze(["пн", "вт", "ср"]);days.push("чт");TypeError: Cannot add property 3, object is not extensibleconst не стига за това: той само не дава на days да сочи друг масив, а самият масив може да се променя.
По-точно, за любопитните
- Текстът се подрежда по кодовете на символите. Главните букви са преди малките, затова „Ябълка“ е преди „ананас“:
const fruit = ["ананас", "Ябълка", "банан", "Круша"];fruit.sort();console.log(fruit);[ 'Круша', 'Ябълка', 'ананас', 'банан' ]toSorted()връща нов, подреден масив и не пипа стария:
const prices = [30, 5, 12];const cheapFirst = prices.toSorted(function (a, b) { return a - b;});console.log(cheapFirst, prices);[ 5, 12, 30 ] [ 30, 5, 12 ]- Как се подреждат твои обекти, например продукти по цена, ще видиш в урока „Класове в класове“.
Упражнение
Масиви в класове
#books), а кодът под класовете вика само методите му.1. Рафт с книги. Клас BookShelf с методи addBook(title), който слага книгата в края, и takeBook(), който връща и маха първата книга (или null, ако рафтът е празен). Добави count() и toString(). Сложи „Под игото“, „Тютюн“ и „Железният светилник“, вземи една книга и отпечатай останалите.
Взе: Под иготоОстават 2: [Тютюн, Железният светилник]2. Журнал на събитията. Клас Event с дата и описание и клас EventLog с методи addEvent(date, description) и getEventsBetween(from, to), който връща масив със събитията между двете дати, включително. Датите са текст във вида 2026-03-31. В този вид текстът се подрежда като датите, затова <= и >= ги сравняват вярно. Добави четири събития: 2026-01-15 „Годишна инвентаризация“, 2026-03-31 „Отчет за първото тримесечие“, 2026-05-20 „Нов доставчик на кафе“, 2026-09-01 „Нов учебен сезон“. Отпечатай тези от 2026-03-01 до 2026-06-30.
2026-03-31 Отчет за първото тримесечие2026-05-20 Нов доставчик на кафе3. Тетрадка. Клас Note със заглавие и съдържание и клас Notebook с методи addNote, findNote(title) (връща бележката или null), deleteNote(title) (връща true, ако е изтрил) и count(). Добави бележки „ООП“, „Колекции“ и „SQL“, намери „Колекции“, изтрий „ООП“ и провери, че вече я няма.
Колекции: масивът расте самИзтрита: ООПБележки: 2Няма бележка ООП.4. Смятане наум. Клас Question с текст и верен отговор и клас MathGame с масив от въпроси, метод addQuestion и метод start(). start задава въпросите един по един, чете отговора и накрая казва колко са верните. Въпросите са 2 + 2, 3 * 4 и 15 - 9.
start чете с ask от урока за входа, затова е async start(), а извикването е await game.start(). Метод, който чака вход, се пише така. Засега го копирай.
41272 + 2 = Вярно!3 * 4 = Вярно!15 - 9 = Грешно, отговорът е 6.Резултат: 2 от 3Още задачи
- Без повторения. Прочети 6 имена и ги събери в масив, като пропускаш тези, които вече са в него. При вход Иван, Мария, Иван, Ана, Мария, Петър изходът е
[ 'Иван', 'Мария', 'Ана', 'Петър' ]. - Най-скъпият продукт. В масив има цени. Намери най-високата и на кой индекс е, без
Math.max. - Обърни списъка. Напиши функция, която връща нов масив със същите елементи в обратен ред. Не променяй първоначалния.
- Списък за пазаруване. Чети команди
add мляко,remove мляко,listиendи пази списъка между тях. Командата за триене на продукт, който го няма, казва „Няма такъв продукт.“
Тест