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

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

Время чтения: меньше 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