Callback-функції, call та apply

Метод forEach

Давайте спробуємо самі створити функцію forEach, щоб детально розібратися як працює метод масива .forEach().

Завдання:
- створити функцію перебору елементів масива;
- у якості першого аргумента виступає масив, який необхідно перебрати;
- у якості другого аргумента виступає callback-функція, якою буде здійснено обробку кожного елемента масива.

В першу чергу створіть call-back функцію, що виводить в консоль квадрат переданого елемента та всі передані в функцію аргументи.

Функція може приймати 1, 2, або 3 аргумента: елемент, ітератор та сам масив.

Метод sort

Давайте спробуємо самі створити функцію Sort, щоб детально розібратися як працює метод масива .sort().

Завдання:
- створити функцію сортування елементів масива;
- у якості першого аргумента виступає масив, який необхідно відсортувати;
- у якості другого аргумента виступає callback-функція, якою буде здійснено обробку елементів масива попарно.

В першу чергу створіть 3 call-back функції, які сортують числа по зростанню, спаданню та в першу чергу виводять непарні, а потім - парні значення.

Функції повинні приймати по 2 аргумента, які будуть порівнювати між собою.

Методи call та apply

https://learn.javascript.ru/call-apply.

Методи call та apply застосовуються до будь-яких функцій чи методів.

function blabla() { ... }    // створення функції
var glagla = function() { ... }    // створення функції
blabla();    // виклик функції
glagla();    // виклик функції
blabla.call();    // виклик метода call для функції
glagla.apply();    // виклик метода apply для функції

Ці два метода "по-своєму" запускають функцію.

Уявіть, що авто - це функція. Коли ви сідаєте за кермо та їдете - це прямий виклик функції.

Тепер уявіть, що в авто посадили робота. Робот - це метод call (чи apply), ви керуєте роботом, а робот керує автомобілем.

У такого способа керування автомобілем є свої недоліки та переваги.

.call();

Виклик здійснюється так:

func.call(context, arg1, arg2, arg3);

Даний метод задає функції контекст (this, батька).

Виконайте наступний код:

function aaa(a, b, c) {
  console.log(this);
  console.log(a, b, c);
}
aaa(5, 8, 42);

var masha = {
  'name': 'Маша',
  age: 17,
  bbb: aaa
};
var petya = {
  'name': 'Петя',
  age: 22,
};

aaa.call(masha, 5, 8, 42);
masha.bbb(8, 8, 42);
masha.bbb.call(petya, 8, 8, 42);

Тобто, за допомогою call можна застосувати будь-яку функцію ніби-то як метод до будь-якого елемента (контекста).

Метод call підміняє контекст ("батька") для функції чи метода.

Ще один варіант практичного застосування:

var p = document.getElementsByTagName('p');

Хочемо перебрати всі пешки і щось з ними зробити.

У змінній p у нас не масив, а HTMLCollection, у нього немає метода forEach, тому доведеться використати цикл.

Або... скористатися методом call, викликавши forEach з прототипа чи іншого масива.

Це є можливим тому, що метод forEach не перевіряє - що ми йому підсовуємо як this: чи справжній масив, чи щось, що просто має елементи та властивість length.

var p = document.getElementsByTagName('p'),
    k = [];
console.log('------------------------------');

k.forEach.call(p, function(e, i){
  console.log(i, e.innerText.slice(0, 20));
});

Більш коректний варіант - звернення до прототипа, не потребує іншого масива:

Array.prototype.forEach.call(p, function(e, i){
  console.log(i, e.innerText.slice(0, 20));
});

Звісно, якщо в якості контекста підсунути число чи об'єкт, то метод не спрацює.

.apply();

Метод .apply() дуже схожий на метод call, але зручніший для роботи з динамічною кількістю аргументів.

func.apply(context, arg_arr);
func.apply(context, [arg1, arg2, arg3]);

Тобто, ви можете передати всі аргументи у вигляді масива.

Це часто потрібно при виклику callback-функцій з довільною кількістю аргументів, детальніше розглянемо далі на методах max та min.

Функції бібліотеки Math.max та Math.min

Для кращого розуміння теми давайте самі створимо функції max та min, які є в бібліотеці Math.

Дані функції приймають довільну кількість аргументів (потрібно передбачити дії коли буде передано 0 або 1 аргумент та якщо є нечислові аргументи).

Серед усіх аргументів відбувається пошук найбільшого чи найменшого значення і повертається це значення.

Домашнє завдання

  1. Самостійно розберіться з методом .bind()
  2. Закінчіть функції min, max, протестуйте на різноманітних масивах.
  3. Посортуйте всі абзаци лекції, використовуючи функцію сортування масива .sort().
  4. Перевірте, чи хоч в одному заголовку h2 є літера "ї". Використайте відповідні методи.
  5. Створіть базовий об'єкт "гарбуз", задайте йому колір, сорт, ціну за кілограм, нульову вагу, а також метод виводу в консоль інормації про вартість гарбуза.
    На основі цього базового об'єкта створіть 5 гарбузів тільки з властивостями, без методів, задавайте кожному випадкову вагу в межах 5-15 кг.
    Скористайтесь методом базового об'єкта для вивода інформації про вартість кожного створеного гарбуза.
  6. Перевірте, чи спрацює метод масивів .forEach() для строк.

Мінотавр в лабіринті.

Мінотавра якось треба позначити. Можна знову перебрати весь лабіринт в пошуках вільних клітин, випадковій вільній клітині задати клас minotaur і запам'ятати координати, наприклад, у mx та my.

Кожен свій хід мінотавр повинен рекурсивно перебрати всі доступні шляхи. Наш лабіринт не є великим, рекурсія не буде дуже глибокою, тому про пам'ять поки не будемо тубруватися.

Створюємо рекурсію, яка з поточної клітини хоче піти вгору, вниз, вліво, вправо.

Ходи рекурсії можна записувати послідовно у масив.

Кожен хід рекурсії треба перевірити:
- чи є вільний прохід на нову клітину;
- чи ми ще не були на тій клітині;
- чи ми знайшли шукача скарбів.

Якщо знайшли шукача скарбів - цю гілку варто зберегти як потенційний шлях до жертви.

В кінці рекурсії потрібно перебрати всі збережені гілки, знайти найкоротшу і зробити крок по її перших координатах: забрати клас у поточної клітини, передати його наступній та змінити mx чи my.

Ну і перевірити - чи не наздогнали ми шукача скарбів. Якщо встали на його клітину - game over.

Один ньюанс: якщо шукач скарбів перейде на клітину з мінотавром - гра повинна завершитися. Чи мінотавр в свій хід повинен перевірити в першу чергу - чи не став шукач скарбів на його клітину.