11'use strict' ;
22
3- // ** Задание 1:
3+ // Задание 1:
44// Как "починить" эти два проблемных вызова?
55// Как нужно изменить строку setTimeout(counter.increment, 100) с помощью bind(), чтобы this не терялся?
66// Как можно было бы сразу вызвать standaloneIncrement с правильным this с помощью call()?
@@ -18,7 +18,7 @@ setTimeout(counter.increment.bind(counter), 100); // 1
1818const standaloneIncrement = counter . increment ;
1919standaloneIncrement . call ( counter ) ; // 2
2020
21- // ** Задание 2:
21+ // Задание 2:
2222// Согласно разметки напиши JavaScript-код, который:
2323// 1. Найдёт оба инпута (#num1, #num2), кнопку (#addBtn) и span для результата (#result).
2424// 2. Добавит обработчик события (addEventListener) на кнопку (#addBtn) по клику ('click').
@@ -39,3 +39,329 @@ addBtnEl.addEventListener('click', () => {
3939 const sum = num1 + num2 ;
4040 resultEl . textContent = sum ;
4141} ) ;
42+
43+ // !! Задание 3:
44+ // Напиши функцию mySort(arr), которая сортирует массив чисел по возрастанию, не используя метод .sort(). Реализуй алгоритм «Пузырьковой сортировки» (Bubble Sort).
45+ // Условия:
46+ // 1. Используй вложенные циклы for.
47+ // 2. Помни про оптимизацию: если массив уже частично отсортирован, лишние итерации не нужны (но для начала сделай хотя бы базовый рабочий вариант).
48+
49+ const bubbleNum = [ 45 , 12 , 3 , 67 , 1 , 9 ] ;
50+
51+ for ( let i = 0 ; i < bubbleNum . length ; i ++ ) {
52+ for ( let j = 0 ; j < bubbleNum . length - 1 - i ; j ++ ) {
53+ if ( bubbleNum [ j ] > bubbleNum [ j + 1 ] ) {
54+ let temp = bubbleNum [ j + 1 ] ;
55+ bubbleNum [ j + 1 ] = bubbleNum [ j ] ;
56+ bubbleNum [ j ] = temp ;
57+ }
58+ }
59+ }
60+
61+ console . log ( bubbleNum ) ; // [1, 3, 9, 12, 45, 67]
62+
63+ // !! Задание 4:
64+ // Пришли "грязные" данные с сервера. В массиве есть дубликаты, пустые строки, null, undefined и прочий мусор.
65+ // Напиши код, который вернет чистый массив уникальных строковых и числовых значений (без 0, null, false и т.д.).
66+ // Требование: Реализуй два разных варианта удаления дубликатов (как раз вспомним способы из конспекта):
67+ // 1. Вариант Modern: Используя структуру Set (самый лаконичный способ).
68+ // 2. Вариант Functional: Используя методы массивов (filter + indexOf).
69+ // Ожидаемый результат (для обоих вариантов): [1, 'apple', 'banana', 'orange']
70+
71+ const dirtyData = [
72+ 1 ,
73+ 0 ,
74+ 'apple' ,
75+ '' ,
76+ 'banana' ,
77+ null ,
78+ 'apple' ,
79+ undefined ,
80+ 'orange' ,
81+ 0 ,
82+ false ,
83+ 'banana' ,
84+ ] ;
85+
86+ const setDirtyData = [ ...new Set ( dirtyData ) ] . filter ( Boolean ) ;
87+ console . log ( setDirtyData ) ; // [1, 'apple', 'banana', 'orange']
88+
89+ const filterDirtyData = dirtyData
90+ . filter ( Boolean )
91+ . filter ( ( value , index , currentArr ) => currentArr . indexOf ( value ) === index ) ;
92+ console . log ( filterDirtyData ) ; // [1, 'apple', 'banana', 'orange']
93+
94+ // !! Задание 5:
95+ // Напиши функцию maskCreditCard(card), которая скрывает все цифры номера карты, кроме последних четырёх.
96+ // Требования:
97+ // 1. Функция должна принимать как строку, так и число (нужна конвертация типов, Source 14 ).
98+ // 2. Используй метод .slice() с отрицательным индексом для получения последних 4 цифр.
99+ // 3. Используй метод .padStart() для заполнения начала строки звёздочками *. Длина итоговой строки должна соответствовать длине исходного номера.
100+
101+ function maskCreditCard ( card ) {
102+ if ( ! card ) {
103+ return '' ;
104+ }
105+
106+ if ( typeof card === 'string' ) {
107+ card = card . trim ( ) ;
108+ }
109+
110+ if ( typeof card === 'number' ) {
111+ card = String ( card ) ;
112+ }
113+
114+ if ( card . length < 4 ) {
115+ return '' ;
116+ }
117+
118+ const cardLength = card . length ;
119+ const lastNum = card . slice ( - 4 ) ;
120+
121+ return lastNum . padStart ( cardLength , '*' ) ;
122+ }
123+
124+ console . log ( maskCreditCard ( '1234567812345678' ) ) ; // "************5678"
125+ console . log ( maskCreditCard ( 4455667788991234 ) ) ; // "************1234"
126+ console . log ( maskCreditCard ( '1234' ) ) ; // "1234"
127+
128+ // !! Задание 6:
129+ // Напиши модуль "Счётчик", который защищает свои данные от прямого изменения извне.
130+ // Требования:
131+ // 1. Используй IIFE (Immediately Invoked Function Expression), чтобы создать закрытую область видимости сразу при объявлении.
132+ // 2. Внутри должна быть приватная переменная count (начальное значение 0).
133+ // 3. Функция должна возвращать объект с тремя методами:
134+ // 4. increase(): увеличивает счётчик на 1.
135+ // 5. decrease(): уменьшает счётчик на 1.
136+ // 6. getValue(): возвращает текущее значение.
137+ // 7. Переменная count не должна быть доступна напрямую (например, counter.count должно быть undefined).
138+
139+ const counterIIFE = ( function ( ) {
140+ let count = 0 ;
141+
142+ return {
143+ increase ( ) {
144+ count ++ ;
145+ } ,
146+ decrease ( ) {
147+ count -- ;
148+ } ,
149+ getValue ( ) {
150+ return count ;
151+ } ,
152+ } ;
153+ } ) ( ) ;
154+
155+ counterIIFE . increase ( ) ;
156+ counterIIFE . increase ( ) ;
157+ console . log ( counterIIFE . getValue ( ) ) ; // 2
158+ console . log ( counterIIFE . count ) ; // undefined
159+
160+ // !! Задание 7:
161+ // У нас есть "Волшебник", который умеет лечить (heal). И есть "Воин", у которого мало здоровья, но нет метода лечения.
162+ // Твоя задача:
163+ // 1. Вылечи воина Артура на 20 HP заклинанием 'Holy Light', используя метод волшебника и .call().
164+ // 2. Вылечи воина Артура ещё на 50 HP заклинанием 'Great Heal', используя метод волшебника и .apply().
165+ // Требование: Обрати внимание на разницу в передаче аргументов между call и apply (запятые vs массив).
166+
167+ const wizard = {
168+ name : 'Merlin' ,
169+ health : 50 ,
170+ heal ( amount , spellName ) {
171+ this . health += amount ;
172+ console . log (
173+ `${ this . name } исцелен на ${ amount } HP с помощью заклинания "${ spellName } ". Текущее здоровье: ${ this . health } `
174+ ) ;
175+ } ,
176+ } ;
177+
178+ const warrior = {
179+ name : 'Arthur' ,
180+ health : 30 ,
181+ } ;
182+
183+ wizard . heal . call ( warrior , 20 , 'Holy Light' ) ; // "Arthur исцелен на 20 HP с помощью заклинания "Holy Light". Текущее здоровье: 50"
184+ wizard . heal . apply ( warrior , [ 50 , 'Great Heal' ] ) ; // "Arthur исцелен на 50 HP с помощью заклинания "Great Heal". Текущее здоровье: 100"
185+
186+ // !! Задание 8:
187+ // Используя тот же объект wizard и его метод heal(amount, spellName), создай новую функцию.
188+ // Требования:
189+ // 1. Создай функцию healArthur50.
190+ // 2. Она должна быть основана на методе wizard.heal.
191+ // 3. Контекст (this) должен быть навсегда привязан к воину Arthur (warrior).
192+ // 4. Первый аргумент (amount) должен быть навсегда зафиксирован на значении 50.
193+ // 5. Вызови полученную функцию healArthur50, передав ей только название заклинания (например, "Super Potion").
194+ // Ожидаемый результат: "Arthur исцелен на 50 HP с помощью заклинания "Super Potion". Текущее здоровье: ..."
195+
196+ const healArthur50 = wizard . heal . bind ( warrior , 50 ) ;
197+ console . log ( healArthur50 ( 'Super Potion' ) ) ; // "Arthur исцелен на 50 HP с помощью заклинания "Super Potion". Текущее здоровье: 150"
198+
199+ // !! Задание 9:
200+ // Твоя задача: Используя только метод .reduce(), создай объект, где ключами будут имена пользователей, а значениями — общая сумма их заказов.
201+ // Алгоритм:
202+ // 1. Начни с пустого объекта {} (initialValue).
203+ // 2. На каждой итерации проверяй: есть ли уже такой user в аккумуляторе?
204+ // 3. Если есть — прибавляй price.
205+ // 4. Если нет — создавай ключ и записывай туда price.
206+ // 5. Не забудь вернуть аккумулятор.
207+ // Ожидаемый результат:
208+ /*
209+ {
210+ Alice: 200,
211+ Bob: 120,
212+ Charlie: 40
213+ }
214+ */
215+
216+ const orders = [
217+ { user : 'Alice' , price : 50 } ,
218+ { user : 'Bob' , price : 100 } ,
219+ { user : 'Alice' , price : 150 } ,
220+ { user : 'Charlie' , price : 40 } ,
221+ { user : 'Bob' , price : 20 } ,
222+ ] ;
223+
224+ const users = orders . reduce ( ( acc , order ) => {
225+ if ( ! acc [ order . user ] ) {
226+ acc [ order . user ] = 0 ;
227+ }
228+
229+ acc [ order . user ] += order . price ;
230+
231+ return acc ;
232+ } , { } ) ;
233+
234+ console . log ( users ) ; // {Alice: 200, Bob: 120, Charlie: 40}
235+
236+ // Задание 10:
237+ // Напиши функцию myFilter(array, callback), которая полностью повторяет логику стандартного метода .filter().
238+ // Требования:
239+ // 1. Функция принимает два аргумента: array (исходный массив) и callback (функция-условие).
240+ // 2. Функция должна возвращать новый массив.
241+ // 3. Внутри функции используй цикл for (или for...of) для перебора элементов.
242+ // 4. callback должен вызываться для каждого элемента. Если callback возвращает true, элемент попадает в новый массив. Если false — игнорируется.
243+ // 5. Важно: callback в стандартном filter принимает 3 аргумента: (item, index, array). Реализуй поддержку всех трёх аргументов при вызове колбэка внутри своей функции.
244+
245+ const numbers = [ 10 , 20 , 5 , 30 , 2 , 15 ] ;
246+ const isBigEnough = ( value , index , arr ) => value >= 10 ;
247+
248+ function myFilter ( array , callback ) {
249+ if ( ! array || ! Array . isArray ( array ) ) {
250+ return [ ] ;
251+ }
252+
253+ if ( ! callback || typeof callback !== 'function' ) {
254+ return [ ] ;
255+ }
256+
257+ const filteredArr = [ ] ;
258+
259+ for ( let i = 0 ; i < array . length ; i ++ ) {
260+ if ( callback ( array [ i ] , i , array ) ) {
261+ filteredArr . push ( array [ i ] ) ;
262+ }
263+ }
264+
265+ return filteredArr ;
266+ }
267+
268+ console . log ( myFilter ( numbers , isBigEnough ) ) ; // [10, 20, 30, 15]
269+
270+ // !! Задание 11:
271+ // Напиши функцию renderTasks(tasks), которая принимает массив строк и отрисовывает их в HTML-списке.
272+ // Требования:
273+ // 1. Внутри функции найди элемент <ul> с id="todo-list" (предположим, он есть в HTML).
274+ // 2. Очисти его текущее содержимое перед отрисовкой (чтобы не дублировать задачи при повторном вызове). Используй innerHTML = '' или textContent = ''.
275+ // 3. Пройдись циклом по массиву tasks.
276+ // 4. Для каждой задачи:
277+ // - Создай новый элемент <li> через document.createElement.
278+ // - Запиши текст задачи внутрь <li> (свойство textContent предпочтительнее, чем innerText, так как работает быстрее и предсказуемее).
279+ // - Добавь этот <li> в конец списка <ul> (метод append или appendChild).
280+
281+ const myTasks = [ 'Сделать рефакторинг' , 'Выпить кофе' , 'Запушить в main' ] ;
282+ const toDoList = document . getElementById ( 'todo-list' ) ;
283+
284+ function renderTasks ( tasksArr ) {
285+ if ( ! tasksArr || ! Array . isArray ( tasksArr ) ) {
286+ return [ ] ;
287+ }
288+
289+ toDoList . innerHTML = '' ; // предварительная очистка
290+ const toDoFragment = document . createDocumentFragment ( ) ;
291+
292+ for ( const task of tasksArr ) {
293+ const toDoItem = document . createElement ( 'li' ) ;
294+ toDoItem . classList . add ( 'todo-item' ) ;
295+ toDoItem . textContent = task ;
296+
297+ toDoFragment . appendChild ( toDoItem ) ;
298+ }
299+
300+ toDoList . append ( toDoFragment ) ;
301+ }
302+
303+ renderTasks ( myTasks ) ;
304+
305+ // !! Задание 12:
306+ // Добавь функционал: при клике на любую задачу (<li>), она должна "зачеркиваться" (добавляем/убираем класс).
307+ // Входящие данные: Используй переменную toDoList из прошлой задачи (это наш <ul>). Предположим, в CSS есть класс:
308+ // .completed { text-decoration: line-through; opacity: 0.5; }
309+ // Требования:
310+ // 1. Навесь слушатель события 'click' только на родительский элемент toDoList.
311+ // 2. Внутри функции-обработчика используй event.target, чтобы понять, куда именно кликнули.
312+ // 3. Проверь, что клик был именно по LI (или внутри него). Подсказка: здесь пригодится метод closest() или проверка tagName.
313+ // 4. Если клик был по задаче — переключи класс 'completed' с помощью classList.toggle().
314+
315+ toDoList . addEventListener ( 'click' , ( event ) => {
316+ const target = event . target ;
317+
318+ // if (target.tagName === 'LI') {
319+ // target.classList.toggle('completed');
320+ // }
321+
322+ const li = target . closest ( 'li' ) ; // исключение проблем, если в li есть ещё/появятся другие элементы
323+
324+ if ( li ) {
325+ li . classList . toggle ( 'completed' ) ;
326+ }
327+ } ) ;
328+
329+ // !! Задание 13:
330+ // Напиши две утилитарные функции для работы с localStorage.
331+ // Требования:
332+ // 1. Функция saveToStorage(key, data):
333+ // - Принимает ключ (строка) и данные (массив или объект).
334+ // - Преобразует данные в строку формата JSON (используй JSON.stringify).
335+ // - Сохраняет в localStorage.
336+ // 2. Функция loadFromStorage(key):
337+ // - Принимает ключ.
338+ // - Получает данные из localStorage.
339+ // - Если данных нет (вернулся null) — возвращает пустой массив [] (чтобы код не падал).
340+ // - Если данные есть — преобразует их обратно из JSON в объект/массив (используй JSON.parse).
341+
342+ const myData = [ { id : 1 , text : 'Code' } ] ;
343+
344+ function saveToStorage ( key , data ) {
345+ if ( ! key || typeof key !== 'string' ) {
346+ return [ ] ;
347+ }
348+
349+ if ( ! data || ! Array . isArray ( data ) ) {
350+ return [ ] ;
351+ }
352+
353+ localStorage . setItem ( key , JSON . stringify ( data ) ) ;
354+ }
355+
356+ function loadFromStorage ( key ) {
357+ if ( ! key || typeof key !== 'string' ) {
358+ return [ ] ;
359+ }
360+
361+ return JSON . parse ( localStorage . getItem ( key ) || '[]' ) ; // очень важное ИЛИ.. указание [] не даст упасть в ошибку
362+ }
363+
364+ saveToStorage ( 'tasks' , myData ) ;
365+
366+ const loaded = loadFromStorage ( 'tasks' ) ;
367+ console . log ( loaded ) ; // [{ id: 1, text: 'Code' }]
0 commit comments