Функция как тип данных

С функцией можно работать, как с любым другим типом данных: сохранять в переменную, передавать, возвращать из функции

Время чтения: меньше 5 мин

Кратко

Секция статьи "Кратко"

Функции — это объект первого класса. Это означает, что функцию можно использовать так же, как и другие типы данных: сохранять в переменную, передавать аргументом и возвращать из функции.

Технически, функция — это объект 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. Даже встроенные методы, такие как forEach и filter используют этот подход.

Другой случай использования — колбэки в асинхронном коде. Иногда необходимо выполнить операцию после того, как закончится какое-то действие. Например, когда пользователь кликнет на кнопку. В этом случае используется метод addEventListener, который принимает имя события и колбэк, который нужно вызвать при его наступлении:

        
          
          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
  }
}

        
        
          
        
      

Здесь очень легко запутаться во вложенности. При вызове lazySum мы передаём два аргумента. Эти аргументы не используются тут же — мы создаём новую функцию, которая складывает два числа и возвращаем её. После вызова lazySum мы можем сохранить эту функцию в переменную и использовать её, когда нужно:

        
          
          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