190.57K
Category: programmingprogramming

Функции и функциональное программирование на JavaScript. Лекция 2

1.

МИРЭА – Российский технологический университет
Институт перспективных технологий и индустриального программирования
09.03.02 «Информационные системы и технологии» - фуллстек разработка
Дисциплина «Технологии индустриального программирования» (3 семестр)
Лекция 2
Функции и функциональное программирование на JavaScript
доцент кафедры индустриального программирования
Юдин Александр Викторович
[email protected]
Москва, 2023 г.

2.

Функции в JavaScript
Функции в JavaScript являются полноправными
значениями, такими же, как числа и строки.
Функции могут принимать и порождать другие
функции. Овладение функциональным
стилем программирования совершенно
необходимо для работы с современным
JavaScript.

3.

Объявление функций
В JavaScript для объявления функции нужно указать:
1) имя функции;
2) имена параметров;
3) тело функции, в котором вычисляется и возвращается ее результат.
Типы параметров и результата не задаются. Пример.
function average(x, y) {
return (x + y) / 2
}
Предложение return служит для возврата вычисленного функцией значения.
Чтобы вызвать эту функцию, нужно передать ей желаемые аргументы:
let result = average(6, 7) // результат равен 6.5
result = average('6', '7') // результат равен 33.5
Функция может не возвращать никакого значения. Если в теле функции нет предложения return, или оно
есть, но не сопровождается выражением, то функция возвращает значение undefined. Обычно так бывает,
когда функция вызывается только ради побочного эффекта.

4.

Функции высшего порядка
JavaScript – функциональный язык программирования. Функции являются значениями, которые можно
сохранять в переменных, передавать в качестве аргументов или возвращать из функции в качестве
значений. Например, функцию average можно сохранить в переменной:
let f = average
А затем вызвать ее:
let result = f(6, 7)
При вычислении выражения f(6, 7) будет обнаружено, что f – функция,и эта функция будет вызвана с
аргументами 6 и 7.
Приведем пример передачи функции в качестве аргумента. Если arr – массив, то вызов метода
arr.map(someFunction)
применяет переданную функцию ко всем элементам и возвращает массив результатов (исходный массив
при этом не изменяется). Например, в результате вызова
result = [0, 1, 2, 4].map(Math.sqrt)
result получает значение [0, 1, 1.4142135623730951, 2]
Метод map иногда называют функцией высшего порядка: это функция, которая получает другую функцию
в качестве параметра.

5.

Функциональные литералы
Пусть требуется умножить все элементы массива на 10. Конечно, можно написать
функцию
function multiplyBy10(x) { return x * 10 }
И вызвать ее следующим образом:
result = [0, 1, 2, 4].map(multiplyBy10)
Но не всегда эффективно объявлять функцию только для того, чтобы использовать ее
всего один раз. Лучше воспользоваться функциональным литералом. Приведем пример:
result = [0, 1, 2, 4].map(function (x) { return 10 * x })
Функциональному литералу можно дать при необходимости имя:
const average = function (x, y) { return (x + y) / 2 }

6.

Стрелочные функции
Существует еще одна, более краткая форма с использованием оператора =>, которая обычно называется
«стрелочной»:
const average = (x, y) => (x + y) / 2
Переменные-параметры располагаются слева от стрелки, а возвращаемое значение – справа. Если параметр всего
один, то заключать его в скобки необязательно:
const multiplyBy10 = x => x * 10
Если у функции нет параметров, то следует оставить пустые скобки:
const dieToss = () => Math.trunc(Math.random() * 6) + 1
Отметим, что dieToss – функция, а не число. При каждом вызове dieToss() мы получаем случайное целое число от 1 до 6.
Если стрелочная функция более сложная, то ее тело следует поместить в блок. Для возврата значения из блока
используется ключевое слово return:
const indexOf = (arr, value) => {
for (let i in arr) {
if (arr[i] === value) return i
}
return -1
}

7.

Функциональная обработка массива
Вместо того чтобы обходить массив в цикле for of или for in, можно воспользоваться методом forEach, передав ему
функцию, которая получает на входе элемент и его индекс:
arr.forEach((element, index) => { console.log(`${index}: ${element}`) })
Эта функция вызывается для каждого элемента массива в порядке возрастания индексов.
Если вас интересуют только сами элементы, то можно передать функцию с одним параметром:
arr.forEach(element => { console.log(`${element}`) })
Метод forEach будет вызывать эту функцию, передавая элемент и индекс, но в данном случае индекс игнорируется.
Метод forEach не возвращает никакого результата. Но переданная ему функция должна иметь какой-то побочный
эффект – напечатать значение или выполнить присваивание. Еще лучше, если есть возможность вообще избежать
побочного эффекта и воспользоваться такими методами, как map или filter, которые преобразуют массив в желаемую
форму. Метод filter получает на входе предикат – функцию, которая возвращает булево (или похожее на булево)
значение. Результатом является массив всех элементов, удовлетворяющих этому предикату.
Пример: построить HTML-список, содержащий элементы массива items
const enclose = (tag, contents) => `<${tag}>${contents}</${tag}>`
const list = enclose('ul',
items
.filter(i => i.trim() !== '')
.map(i => enclose('li', i))
.join(‘’)) //конкатенирует все строки

8.

Замыкание
Функция setTimeout принимает два аргумента: функцию, которую нужно будет выполнить позже, когда истечет
таймаут, и продолжительность таймаута в миллисекундах. Например, следующий вызов напечатает «Goodbye» через
10 ескунд:
setTimeout(() => console.log('Goodbye'), 10000)
Усовершенствуем код:
const sayLater = (text, when) => {
let task = () => console.log(text)
setTimeout(task, when)
}
У функции есть три составляющие:
1) блок кода;
2) параметры;
3) свободные переменные, т. е. переменные, которые встречаются в коде, но не объявлены как параметры или
локальные переменные. Функция со свободными переменными называется замыканием.
В нашем примере text – свободная переменная стрелочной функции. В структуре данных, представляющей замыкание,
хранится ссылка на переменную, существовавшую в момент создания функции. Мы говорим, что эта переменная
захвачена. Поэтому ее значение доступно в момент, когда функция будет вызвана.

9.

Паттерн фабричного класса (паттерн замыкания)
У объектов JavaScript нет закрытых свойств. Модифицировать объекты может кто угодно. Рассмотрим способ, который позволит инкапсулировать данные.
Данные будут храниться в виде локальной переменной функции
Пример: реализовать объекты, представляющие банковские счета
Заготовка для функции:
const createAccount = (initialBalance) => {
let balance = initialBalance
return {
deposit: amount => {
balance += amount
},
withdraw: amount => {
if (balance >= amount)
balance -= amount
},
getBalance: () => balance
}}
const harrysAccount = createAccount()
const sallysAccount = createAccount()
sallysAccount.deposit(500)

10.

Строгий режим
Чтобы включить строгий режим, необходимо поставить первой отличной от комментария строкой в своем
файле команду
'use strict’
Присваивание значения не объявленной ранее переменной считается ошибкой, при этом глобальная
переменная не создается. Любую переменную следует объявлять с помощью одного из ключевых
слов – let, const или var.
Запрещается присваивать новое значение глобальному свойству, предназначенному только для
чтения, например NaN или undefined.
Функции можно объявлять только на верхнем уровне скрипта или функции, но не во вложенном
блоке.
Оператор delete нельзя применять к «неквалифицированным идентификаторам». Например, delete
parseInt является синтаксической ошибкой. Попытка удалить с помощью delete
«неконфигурируемое» свойство (например, delete 'Hello'.length) приводит к ошибке во время
выполнения.
Запрещаются повторяющиеся параметры функции (function average(x,x)). Разумеется, это никогда и
не нужно, но в нестрогом (прощающем небрежность) режиме это допустимо.
Запрещается предложение with (устаревшая конструкция для модификации области видимости)

11.

Проверка типов аргументов
В JavaScript не нужно задавать типы аргументов функции. Поэтому вызывающая сторона может передавать аргумент то одного, то другого
типа, а функция будет обрабатывать его в соответствии с фактическим типом. Так можно написать функцию average, которая готова
принимать как числа, так и массив (хотя это несколько искусственный пример):
const average = (x, y) => {
let sum = 0
let n = 0
if (Array.isArray(x)) {
for (const value of x) { sum += value; n++ }
} else {
sum = x; n = 1
}
if (Array.isArray(y)) {
for (const value of y) { sum += value }
} else {
sum += y; n++
}
return n === 0 ? 0 : sum / n
}
result = average(1, 2); result = average([1, 2, 3], 4); result = average(1, [2, 3, 4]); result = average([1, 2], [3, 4, 5])

12.

Проверка типов аргументов

13.

Передача большего или меньшего числа аргументов
Предположим, что функция объявлена с определенным числом параметров, например:
const average = (x, y) => (x + y) / 2
Функцию можно вызвать с большим числом аргументов, лишние будут молча
проигнорированы:
let result = average(3, 4, 5) // 3.5 – последний аргумент игнорируется
Наоборот, если передать меньше аргументов, то недостающим будет присвоено
значение undefined. Например, average(3)равно (3 + undefined) / 2 или NaN.
Корректно такую ситуацию можно обработать, например, так:
const average = (x, y) => y === undefined ? x : (x + y) / 2

14.

Аргументы по умолчанию
Вместо того чтобы вручную проверять, равен ли аргумент undefined, можно предоставить параметры по
умолчанию в объявлении функции. После параметра добавьте знак = и выражение, подразумеваемое по
умолчанию, – оно будет использоваться, если аргумент не передан.
const average = (x, y = x) => (x + y) / 2
Если вызвать average(3), то y получит значение x, т. е. 3, и будет возвращен правильный результат.
Значений по умолчанию может быть несколько:
const average = (x = 0, y = x) => (x + y) / 2
Теперь average() возвращает 0.
Можно даже предоставить значение по умолчанию для первого параметра и не предоставлять для других:
const average = (x = 0, y) => y === undefined ? x : (x + y) / 2
Если аргумент (в т. ч. равный undefined) не передан, то параметру присваивается значение по умолчанию, а
если его нет, то undefined:
average(3) // average(3, undefined)
average() // average(0, undefined)
average(undefined, 3) // average(0, 3)

15.

Прочие параметры и оператор расширения
Функцию в JavaScript можно вызывать с любым числом аргментов. Чтобы обработать их все, объявите в
качестве последнего параметра функции «прочие», предварив его лексемой …:
const average = (first = 0, ...following) => {
let sum = first
for (const value of following) { sum += value }
return sum / (1 + following.length)
}
При вызове этой функции параметр following является массивом, который содержит все фактические
аргументы, которые не использовались для инициализации предыдущих формальных параметров.
Например, рассмотрим вызов
average(1, 7, 2, 9)
Тогда first будет равно 1, а following – массиву [7, 2, 9].

16.

Прочие параметры и оператор расширения
Многие функции и методы принимают переменное число аргументов.
Например, функция Math.max возвращает наибольший из переданных аргументов, сколько бы их ни было:
let result = Math.max(3, 1, 4, 1, 5, 9, 2, 6) // result равно 9
А что, если значением уже является массив?
let numbers = [1, 7, 2, 9]
result = Math.max(numbers) // возвращает NaN
Это не работает. Метод Math.max получает массив с одним элементом – массивом [1, 7, 2, 9].
Вместо этого воспользуйтесь оператором «расширения» – поставьте лексему … перед массивовым
аргументом:
result = Math.max(...numbers) // возвращает 9
Оператор расширения линеаризует элементы, как если бы они передавались по отдельности.

17.

Возбуждение исключений
Если функция не может вычислить результат, она вправе возбудить исключение. В
зависимости от характера ошибки это может оказаться лучшей стратегией, чем возврат
кода ошибки, например NaN или undefined.
Для возбуждения исключения служит предложение throw:
throw value
Значение исключения может иметь любой тип, но традиционно в этом качестве
используется объект ошибки. Функция Error порождает такой объект, содержащий строку
с описанием причины ошибки.
let reason = `Элемент ${elem} не найден`
throw Error(reason)
После выполнения предложения throw функция немедленно завершается. Не
возвращается никакого значения, даже undefined. Выполнение продолжается не с места
вызова функции, а в ближайшей ветви catch или finally

18.

Перехват исключений
Для перехвата исключений предназначено предложение try. Для анализа исключения пр меняется следующая
конструкция:
try {
// Сделать что-то
...
} catch (e) {
// Обработать исключения
...
}
Переменная в ветви catch (здесь e) содержит значение исключения.
Например, если вызвать
JSON.parse('{ age: 42 }’)
то будет возбуждено исключение с именем 'SyntaxError' и сообщением 'Unexpected token a in JSON at position 2'. (В этом
примере строка является недопустимой в JSON, потому что ключ age не заключен в двойные кавычки.)
При входе в ветвь catch исключение считается обработанным. Обработка продолжается с этой точки, т. е. выполняются
предложения в ветви catch. Из ветви catch можно выйти с помощью предложения return или break либо дойдя до ее
конца. В последнем случае управление передается следующему за catch предложению.

19.

Ветвь finally
В предложении try может быть необязательная ветвь finally. Код в ветви finally выполняется вне
зависимости от того, произошло исключение или нет. Начнем с простейшего случая: предложение try с
ветвью finally, но без ветви catch:
try {
// Захватить ресурсы
...
// Что-то сделать
...
} catch (e) {
// обработать исключение
} finally {
// Освободить ресурсы
...
}
Если в ветви try возникло исключение, то выполняется ветвь catch. Вне зависимости от способа выхода из
catch (нормально или с помощью return/break/throw) далее выполняется ветвь finally.
Назначение ветви finally – организовать единственное место для освобождения ресурсов (например,
описателей файлов или подключений к базе данных), захваченных в ветви try, – не важно, произошло
ислключение или нет

20.

Благодарю за внимание
English     Русский Rules