034.3 Урок 2
Сертифікат: |
Основи веброзробки |
---|---|
Версія: |
1.0 |
Розділ: |
034 JavaScript-програмування |
Тема: |
034.3 Структури та функції керування JavaScript |
Урок: |
2 з 2 |
Вступ
На додаток до стандартного набору вбудованих функцій, наданих мовою JavaScript, розробники можуть писати власні користувацькі функції для відображення вхідних даних у вихідні дані, потрібні програмі. Користувацькі функції – це в основному набір операторів, інкапсульованих для використання в іншому місці у якості частини виразу.
Використання функцій – це хороший спосіб уникнути написання повторюваного коду, оскільки їх можна викликати з різних місць у програмі. Більше того, групування операторів у функціях полегшує прив’язування користувальницьких дій до подій, що є центральним аспектом програмування на JavaScript.
Визначення функції
У міру розвитку програми стає важче організувати те, що вона робить без використання функцій. Кожна функція має власну приватну область дії змінних, тому змінні, визначені всередині функції, будуть доступні лише всередині цієї ж функції. Таким чином, вони не будуть змішуватися зі змінними з інших функцій. Глобальні змінні все ще доступні зсередини функцій, але найкращим способом надсилання вхідних значень до функції є параметри функції. Як приклад, ми збираємося побудувати валідатор простих чисел з попереднього уроку:
// Примітиіний валідатор простих чисел
// Число, яке ми плануємо оцінити
let candidate = 231;
// Допоміжна змінна
let is_prime = true;
// Розпочинаємо з найменшого простого числа після 1
let factor = 2;
// Продовжуємо оцінювати, поки дільник менший за число-кандидат
while ( factor < candidate )
{
if ( candidate % factor == 0 )
{
// Залишок дорівнює нуль, тому кандидат не є простим числом
is_prime = false;
break;
}
// Наступне число, на яке спробуємо ділити кандидата
factor++;
}
// Відображення результату у вікні консолі
if ( is_prime )
{
console.log(candidate, "є простим числом");
}
else
{
console.log(candidate, "не є простим числом");
}
Якщо пізніше в коді вам потрібно перевірити, чи є число простим, потрібно буде повторити вже написаний код. Така практика не рекомендується, оскільки будь-які виправлення або покращення оригінального коду потрібно буде реплікувати вручну всюди, куди було скопійовано код. Більше того, повторення коду створює навантаження на браузер і мережу, що може уповільнити відображення вебсторінки. Замість цього перемістіть відповідні оператори до функції:
// Функція для примітивного тестування простих чисел
function test_prime(candidate)
{
// Допоміжна змінна
let is_prime = true;
// Розпочинаємо з найменшого простого числа після 1
let factor = 2;
// Продовжуємо оцінювати, поки дільник менший за число-кандидат
while ( factor < candidate )
{
if ( candidate % factor == 0 )
{
// Залишок дорівнює нуль, тому кандидат не є простим числом
is_prime = false;
break;
}
// Наступне число, на яке будемо ділити число-кандидат
factor++;
}
// Повернення відповіді
return is_prime;
}
Оголошення функції починається з оператора function
, за яким слідує ім’я функції та її параметри. Ім’я функції має відповідати тим же правилам, що й імена змінних. Параметри функції, також відомі як аргументи функції, які відокремлюються комами й охоплюються дужками.
Tip
|
Перелік аргументів в оголошенні функції не є обов’язковим. Аргументи, передані функції, можна отримати з об’єкта |
У цьому прикладі функція test_prime
має лише один аргумент: аргумент candidate
, який є кандидатом для перевірки на просте число. Аргументи функції працюють як змінні, але їх значення призначаються оператором, що викликає функцію. Наприклад, оператор test_prime(231)
викличе функцію test_prime
і призначить значення 231 аргументу candidate
, який потім буде доступний у тілі функції, як звичайна змінна.
Якщо оператор, що викликає функцію, використовує прості змінні для параметрів функції, їх значення будуть скопійовані до аргументів функції. Ця процедура, копіювання значень параметрів, що використовуються в операторі виклику, до параметрів, що використовуються всередині функції, називається передавання аргументів за значенням. Будь-які зміни, внесені функцією до аргументу, не впливають на оригінальну змінну, використану в операторі, що викликає функцію. Однак, якщо оператор, що викликає, використовує складні об’єкти в якості аргументів (тобто об’єкт із властивостями та методами, доданими до нього) для параметрів функції, вони будуть передані за покликання і функція зможе змінити оригінальний об’єкт, використаний у операторі виклику функції.
Аргументи, які передаються за значенням, а також змінні, оголошені всередині функції, не видно за її межами. Тобто їх область дії обмежена тілом функції, де вони були оголошені. Тим не менш, функції зазвичай використовуються для створення певного результату, видимого за межами функції. Щоб поділитися результатом з викликаючою функцією, функція визначає оператор return
.
Наприклад, функція test_prime
у попередньому прикладі повертає значення змінної is_prime
. Таким чином, функція може замінити змінну будь-де, де вона була використана в оригінальному прикладі:
// Число, яке ми плануємо оцінити
let candidate = 231;
// Відображення результату у вікні консолі
if ( test_prime(candidate) )
{
console.log(candidate, "є простим числом");
}
else
{
console.log(candidate, "не є простим числом");
}
Оператор return
, як випливає з його назви, повертає керування функції, що викликає. Таким чином, де б не було розміщено оператор return
у функції, нічого що розташовано після нього не виконується. Функція може містити кілька операторів return
. Ця практика може бути корисною, якщо деякі з них знаходяться в умовних блоках операторів, тож функція може виконувати або не виконувати певний оператор return
під час кожного запуску.
Деякі функції можуть не повертати значення, тому оператор return
не є обов’язковим. Внутрішні оператори функції виконуються незалежно від його присутності, тому функції також можна використовувати для зміни значень глобальних змінних або вмісту об’єктів, що передаються за покликанням, наприклад. Незважаючи на це, якщо функція не має оператора return
, її значення, що повертається за замовчуванням, встановлюється в undefined
: це зарезервована змінна, яка не має значення і не може бути записана.
Функціональні вирази
В JavaScript, функції – це просто інший тип об’єкту. Таким чином, функції можна використовувати в сценарії, як змінні. Ця характеристика стає явною, коли функція оголошується з використанням альтернативного синтаксису, який називається функціональний вираз:
let test_prime = function(candidate)
{
// Допоміжна змінна
let is_prime = true;
// Розпочинаємо з найменшого простого числа після 1
let factor = 2;
// Продовжуємо оцінювати, поки дільник менший за число-кандидат
while ( factor < candidate )
{
if ( candidate % factor == 0 )
{
// Залишок дорівнює нуль, тому кандидат не є простим числом
is_prime = false;
break;
}
// Наступне число, на яке будемо ділити число-кандидат
factor++;
}
// Повертаємо результат
return is_prime;
}
Єдина відмінність між цим прикладом та оголошенням функції в попередньому прикладі полягає в першому рядку: let test_prime = function(candidate)
замість function test_prime(candidate)
. У функціональному виразі ім’я test_prime
використовується для об’єкта, що містить функцію, а не для назви самої функції. Функції, визначені у функціональних виразах, викликаються так само, як і функції, визначені за допомогою синтаксису оголошення. Однак, якщо оголошені функції можна викликати до або після їх оголошення, то функціональні вирази можуть бути викликані лише після їх ініціалізації. Як і у випадку зі змінними, виклик функції, визначеної у виразі до її ініціалізації, призведе до помилки покликання (reference error).
Рекурсія функцій
На додаток до виконання операторів і виклику вбудованих функцій, користувацькі функції можуть також викликати інші користувацькі функції, включно із собою. Виклик функції з неї самої називається рекурсія функції. Залежно від типу проблеми, яку ви намагаєтеся вирішити, використання рекурсивних функцій може бути більш простим, ніж використання вкладених циклів для виконання повторюваних завдань.
Поки що ми знаємо, як використовувати функцію, щоб перевірити, чи є задане число простим. Тепер припустимо, що ви хочете знайти наступне просте число після заданого числа. Ви можете застосувати цикл while
, щоб збільшити число-кандидат і написати вкладений цикл, який шукатиме цілі дільники для цього кандидата:
// Ця функція повертає наступне просте число
// після числа, вказаного як єдиний аргумент
function next_prime(from)
{
// Нас цікавлять лише додатні прості числа,
// тому число 2 будемо вважати наступним
// простим числом після будь-якого числа меншого двох.
if ( from < 2 )
{
return 2;
}
// Число 2 - це єдине парне додатнє просте число,
// тому буде легше розгляднути його окремо.
if ( from == 2 )
{
return 3;
}
// Зменшуємо "from" на одиницю, якщо це парне число
if ( from % 2 == 0 )
{
from--;
}
// Починаємо пошук простих чисел, більших за 3.
// Кандидатом на просте число є наступне непарне число
let candidate = from + 2;
// "true" продовжує цикл, поки не буде знайдено просте число
while ( true )
{
// Додаткова змінна для контролю
let is_prime = true;
// "candidate" – непарне число, тому в циклі будуть протестовані
// тільки непарні дільники, починаючи з 3
for ( let factor = 3; factor < candidate; factor = factor + 2 )
{
if ( candidate % factor == 0 )
{
// Залишок дорівнює нулю, тому кандидат не є простим числом.
// Перевіримо наступного кандидата
is_prime = false;
break;
}
}
// Кінець циклу і повертаємо candidate, якщо це просте число
if ( is_prime )
{
return candidate;
}
// Якщо просте число ще не знайдено, перевіряємо наступне непарне число
candidate = candidate + 2;
}
}
let from = 1024;
console.log("Наступним простим числом після", from, "є", next_prime(from));
Зауважте, що нам потрібно використовувати постійну умову для циклу while
(вираз true
всередині дужок) і допоміжну змінну is_prime
, щоб знати, коли зупинити цикл. Хоча це рішення правильне, використання вкладених циклів виглядає не так елегантно, як використання рекурсії для виконання того ж завдання:
// Ця функція повертає наступне просте число
// після числа, заданого в її єдиному аргументі
function next_prime(from)
{
// Нас цікавлять тільки додатні прості числа,
// тому розглядаємо число 2 як наступне
// просте число після будь-якого числа, меншого два.
if ( from < 2 )
{
return 2;
}
// Число 2 - це єдине парне додатнє просте число,
// тому буде легше розгляднути його окремо.
if ( from == 2 )
{
return 3;
}
// Зменшуємо "from" на одиницю, якщо це парне число
if ( from % 2 == 0 )
{
from--;
}
// Починаємо пошук простих чисел, більших за 3.
// Наступний кандидат в прості числа - наступне непарне число
let candidate = from + 2;
// "candidate" - непарне число, тому в циклі будуть
// протестовані тільки непарні дільники, починаючи з 3
for ( let factor = 3; factor < candidate; factor = factor + 2 )
{
if ( candidate % factor == 0 )
{
// Залишок дорівнює нулю, тож кандидат не є простим числом.
// Викликаємо функцію next_prime рекурсивно, цього разу
// використовуємо невдалого кандидата в якості аргументу.
return next_prime(candidate);
}
}
// "candidate" не ділиться націло на жоден цілий дільник, який відрізняється від 1
// та саиого себе, тому це просте число.
return candidate;
}
let from = 1024;
console.log("Наступним простим числом після", from, "є", next_prime(from));
Обидві версії next_prime
повертають наступне просте число після числа, вказаного як єдиний аргумент функції (from
). Рекурсивна версія, як і попередня версія, починається з перевірки особливих випадків (тобто чисел, менших або рівних двом). Потім збільшують кандидата і починають шукати будь-які цілі дільники за допомогою циклу for
(зверніть увагу, що циклу while
більше немає). На цей момент єдине парне просте число вже було перевірено, тому кандидат і його можливі дільники збільшуються на два. (Непарне число плюс два є наступним непарним числом.)
У цьому прикладі є лише два шляхи виходу з циклу for
. Якщо всі можливі дільники перевірені, і жоден з них не дав залишку, рівного нулю, при діленні кандидата, цикл for
завершується, і функція повертає кандидата як наступне просте число після from
. В іншому випадку, якщо factor
є цілим дільником candidate
(candidate % factor == 0
), значення, що повертається, надходить від функції next_prime
, викликаної рекурсивно, цього разу зі збільшеним на 1 значенням candidate
в якості її параметра from
. Виклики next_prime
будуть накладені один на одного, доки один з кандидатів не виявить жодного цілого множника. Тоді останній екземпляр next_prime
, що містить просте число, поверне його до попереднього екземпляра next_prime
і, таким чином, послідовно до першого екземпляра next_prime
. Незважаючи на те, що кожен виклик функції використовує однакові імена для змінних, виклики ізольовані один від одного, тому їх змінні зберігаються в пам’яті окремо.
Вправи до посібника
-
Які накладні витрати можуть зменшити розробники за допомогою функцій?
-
Яка різниця між аргументами функції, що передаються за значенням, і аргументами функції, що передаються за покликанням?
-
Яке значення буде використано як результат користувацької функції, якщо вона не має оператора return?
Дослідницькі вправи
-
Яка ймовірна причина помилки Uncaught Reference Error під час виклику функції, оголошеної за допомогою синтаксису функціональних виразів?
-
Напишіть функцію під назвою
multiples_of
, яка отримує три аргументи:factor
,from
, іto
. Усередині функції використайте інструкціюconsole.log()
, щоб надрукувати всі кратніfactor
числа, що лежать міжfrom
іto
.
Підсумки
Цей урок розглядає написання спеціальних функцій в коді JavaScript. Користувацькі функції дозволяють розробнику розділити програму на “chunks” коду, придатного для повторного використання, що полегшує написання та обслуговування програм більшого розміру. Урок охоплює такі поняття та процедури:
-
Як визначити спеціальну функцію: оголошення функцій і функціональні вирази.
-
Використання параметрів як вхідних даних функції.
-
Використання оператора
return
для встановлення результату функції. -
Рекурсія функцій.
Відповіді до вправ посібника
-
Які накладні витрати можуть зменшити розробники за допомогою функцій?
Функції дають змогу нам повторно використовувати код, що полегшує обслуговування коду. Менший розмір файлу сценарію також економить пам’ять і час завантаження.
-
Яка різниця між аргументами функції, що передаються за значенням, і аргументами функції, що передаються за покликанням?
При передачі за значенням аргумент копіюється до функції, і функція не може змінити оригінальну змінну, яка була використана в операторі, що викликає функцію.. При передачі за покликанням функція може маніпулювати оригінальною змінною, яка була використана в операторі, що викликає функцію.
-
Яке значення буде використано як результат користувацької функції, якщо вона не має оператора return?
Повернене значення буде встановлено в
undefined
.
Відповіді до дослідницьких вправ
-
Яка ймовірна причина помилки Uncaught Reference Error під час виклику функції, оголошеної за допомогою синтаксису функціональних виразів?
Виклик функції відбувся до її оголошення у файлі сценарію.
-
Напишіть функцію під назвою
multiples_of
, яка отримує три аргументи:factor
,from
, іto
. Усередині функції використайте інструкціюconsole.log()
, щоб надрукувати всі кратніfactor
числа, що лежать міжfrom
іto
.function multiples_of(factor, from, to) { for ( let number = from; number <= to; number++ ) { if ( number % factor == 0 ) { console.log(factor, "*", number / factor, "=", number); } } }