Кратко
СкопированоФункции — это объект первого класса. Это означает, что функцию можно использовать так же, как и другие типы данных: сохранять в переменную, передавать аргументом и возвращать из функции.
Технически, функция — это объект JavaScript, у которого есть внутренний метод Call
, который добавляет возможность вызова функции.
Если вы хотите узнать о синтаксисе функций, читайте статью function
.
Как понять
СкопированоВо многих языках функции — это специальные конструкции языка. Они не являются типом данных, и набор операций, которые с ними можно делать ограничен — их можно только объявлять и вызывать.
В JavaScript функция — это тип данных, примерно такой же как объект или строка. Это означает, что с ним можно работать так же, как и с любым другим типом данных — сохранять в переменную, передавать в качестве аргумента функции, возвращать из функций.
О функции удобно думать как об объекте, который поддерживает операцию вызова.
Хранение функции в переменной
СкопированоФункции можно объявлять различными способами. Объявление функции с помощью функционального выражения не что иное, как присваивание безымянной функции переменной:
const answer = function() { console.log('42!')}answer()// 42!
const answer = function() { console.log('42!') } answer() // 42!
Можно сохранять в переменную и функцию, объявленную другим способом. При этом оба имени функции будут работать:
function answerNumber() { console.log('42!')}const answer = answerNumberanswerNumber()// 42!answer()// 42!
function answerNumber() { console.log('42!') } const answer = answerNumber answerNumber() // 42! answer() // 42!
Переменная хранит ссылку на функцию, поэтому мы можем создавать столько переменных, сколько нам нужно и все они будут именами функции:
const answer = function() { console.log('42!')}const answerNumber = answerconst fn = answer
const answer = function() { console.log('42!') } const answerNumber = answer const fn = answer
Передача функции в вызов другой функции
СкопированоФункция может передаваться в качестве аргумента при вызове другой функции.
Например, функция, которая может выполнить произвольную операцию между двумя числами. Два числа хранятся внутри функции, а операция, которую нужно выполнить, передаётся при вызове:
function performOperation(operation) { const a = 10 const b = 99 return operation(a, b)}const sum = performOperation(function(one, two) { return one + two })console.log(sum)// 109const result = performOperation(function(num1, num2) { return num1 ** (num1 / num2)})console.log(result)// 1.2618568830660204
function performOperation(operation) { const a = 10 const b = 99 return operation(a, b) } const sum = performOperation(function(one, two) { return one + two }) console.log(sum) // 109 const result = performOperation(function(num1, num2) { return num1 ** (num1 / num2)}) console.log(result) // 1.2618568830660204
Таким образом логика операции может определяться вне функции, что делает её гибкой.
Функции, которые ожидают получить другую функцию в качестве параметра — стандартное явление в JavaScript. Даже встроенные методы, такие как for
и filter
используют этот подход.
Другой случай использования — колбэки в асинхронном коде. Иногда необходимо выполнить операцию после того, как закончится какое-то действие. Например, когда пользователь кликнет на кнопку. В этом случае используется метод add
, который принимает имя события, и колбэк, который нужно вызвать при его наступлении:
document.getElementsByTagName('button')[0].addEventListener('click', function() { console.log('Пользователь кликнул!')})
document.getElementsByTagName('button')[0].addEventListener('click', function() { console.log('Пользователь кликнул!') })
Возвращение функции как результат вызова
СкопированоФункцию можно вернуть как результат работы другой функции. Например, можно сохранить данные для математической операции, но не выполнять её сразу, а вернуть функцию, которая выполнит операцию над указанными числами:
function lazySum(a, b) { return function() { return a + b }}
function lazySum(a, b) { return function() { return a + b } }
Здесь очень легко запутаться во вложенности. При вызове lazy
мы передаём два аргумента. Эти аргументы не используются тут же — мы создаём новую функцию, которая складывает два числа и возвращаем её. После вызова lazy
мы можем сохранить эту функцию в переменную и использовать её, когда нужно:
const performSum = lazySum(99, 1)console.log(performSum)// function lazySum()console.log(performSum())// 100
const performSum = lazySum(99, 1) console.log(performSum) // function lazySum() console.log(performSum()) // 100
Обратите внимание, что значения параметров a
и b
остаются доступны внутри вложенной функции. Эта особенность связана с контекстом выполнения и лексическим окружением функции. Такой подход также активно используется при разработке на JavaScript.
На практике
Скопированосоветует Скопировано
🛠 Чтобы понять, что в переменной хранится функция, достаточно воспользоваться оператором typeof
. Для функций он возвращает строку 'function'
:
const answer = function() { console.log('42!')}console.log(typeof answer)// 'function'
const answer = function() { console.log('42!') } console.log(typeof answer) // 'function'
🛠 Так как функция технически является объектом, то у функции есть свойства и методы. Например, свойство length
вернёт количество параметров функции:
const answer = function() { console.log('42!')}console.log(answer.length)// 0const sum = function(a, b) { return a + b}console.log(sum.length)// 2
const answer = function() { console.log('42!') } console.log(answer.length) // 0 const sum = function(a, b) { return a + b } console.log(sum.length) // 2
🛠 Функциям можно добавлять свойства как обычным объектам. Такой код встречается редко, но не удивляйтесь, если увидите:
const calc = function() {}calc.type = 'numbers'console.log(calc.type)// numbers
const calc = function() {} calc.type = 'numbers' console.log(calc.type) // numbers