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

Давайте обсудим типы функций и способы их использования:

Объявление функции
function A () {};

Выражение функции
var B = function () {};

Выражение функции с операторами группировки
var C = (function () {});

Выражение именованной функции
var D = function foo () {};

IIFE
var E = (function () {
return function () {}
}) ();

Конструктор функций
var F = new Function ();

Особый случай: конструктор объектов
var G = new function () {};

Стрелочная функция ES6
var H = x = ›x * 2;

Объявление функции

Объявления функций, вероятно, самый известный и самый старый способ делать что-то в мире JavaScript. Это создает переменную A, которая доступна в текущей области.

Подъем
Интересно то, что они «поднимаются» на вершину своего диапазона:

A();
function A() {
  console.log(‘foo’);
};

Вышеупомянутое выполняется как:

function A() {
  console.log(‘foo’);
};
A();

Это означает, что вы можете вызывать функции до того, как они будут написаны в вашем коде. Это не имеет значения, потому что вся функция поднимается до вершины содержащей ее области видимости. (Это контрастирует с переменными, у которых поднимается только их объявление, но не их содержимое).

Никаких объявлений функций в операторах if (или циклах и т. д.)
Вы не можете определять функции таким образом в выражениях, например, в операторах if, что является обычным явлением, если мы хотим определить разные версии функция для разных обстоятельств, обычно для устранения несоответствий в браузере. Что ж, вы можете в некоторых реализациях, но способ обработки кода непоследователен. Если вы хотите использовать этот шаблон, используйте вместо него функциональные выражения.

Объявления функций должны иметь имена
Этот метод не позволяет создавать анонимные функции, а это означает, что вы всегда должны давать ему идентификатор (в данном случае мы использовали «A») .

Выражение функции

Выражение функции похоже на объявление функции, за исключением того, что функция назначается имени переменной. Хотя функции не являются примитивными значениями в JavaScript, именно так они могут быть использованы в полной мере на этом функциональном языке.

JavaScript поддерживает передачу функций в качестве аргументов другим функциям, возвращая их как значения из других функций и присваивая их переменным или сохраняя их в структурах данных.

var B = function(){};

Анонимные функции (им не нужны имена)
Имя функции не является обязательным в выражениях функций, и мы называем их анонимными. В приведенном выше примере мы устанавливаем переменную B равной анонимной функции.

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

var A = function() {...};
var B = function() {...};
var C = function() {...};

Приведенный выше код будет выполнен как

var A, B, C; // variable declarations are hoisted
A = function() {...};
B = function() {...};
C = function() {...};

Поэтому важен порядок установки и вызова этого типа функции:

// this works
var B = function() {};
B();
// this doesn’t work
B2(); // TypeError (B2 is undefined)
var B2 = function() {};

Второй пример дает нам ошибку, потому что поднимается только объявление переменной B2, но не определение, поэтому возникает ошибка «undefined».

Функциональные выражения с операторами группировки

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

var B = (function() {});

Когда движок JavaScript встречает здесь открывающую скобку, мы, по сути, говорим «начните группировать это вместе с чем-то еще». Мы сообщаем движку, что делаем не объявление функции, а выражение функции. А затем присвойте результат переменной:

(function(){});  //resulting function not assigned
var foo = (function(){}); //resulting function assigned to foo
var bar = function(){};  //resulting function assigned to bar

Здесь мы видим, что foo и bar на самом деле одно и то же, потому что в foo мы не группируем функцию вместе ни с чем, кроме самой себя.

Выражение именованной функции

Здесь вместо присвоения переменной анонимной функции мы назначаем ее именованной функции.

var D = function foo() {};

Имя функции доступно только внутри функции
Мы не открыли имя функции (foo) для включающей области (в данном случае глобальной):

var D = function foo() {
  console.log(type of foo);
};
D();                      // function
console.log(typeof foo);  // undefined

Полезно для рекурсии
Поскольку имя функции доступно в самой функции, это оказывается полезным для рекурсивных функций.

var countdown = function a (count) {
  if (count > 0) {
    count--;
    return a(count);  //we can also do this: a(--count)
  }
  console.log(‘End of recursive function’);
}
countdown(5);

Выражение немедленно вызываемой функции (IIFE)

Выполните эту функцию, возвращаемое значение которой является другой функцией, и присвойте ее переменной. Этот паттерн мощный и полезный. Самым известным из них является шаблон модуля.

var E = (function() {
  return function(){
    ...
  }
})();

Мы уже узнали об операторах группировки выше, поэтому вы можете спокойно сказать, что это эквивалентно.

var foo = function() {
  return ‘bar’;
};
var output = foo();
console.log(output); //bar

Поскольку foo указывает на выражение нашей функции, мы знаем, что можем просто воздержаться от использования переменной «foo» и добавить всю функцию как анонимную (поскольку в конце концов, функции являются первоклассными!).

Конструктор функций

Этот метод очень старый, и его не рекомендуется использовать. Вы передаете неограниченное количество аргументов впереди, тогда фактическое тело функции отображается в виде строки в последнем аргументе (поскольку это строка, она фактически эквивалентна eval () и не рекомендуется).

Определение функции
Вы можете создать такую ​​функцию

var F = new Function('arg1', 'arg2', 'console.log(arg1 + ", " + arg2)');
F('foo', 'bar');  // 'foo, bar'

Вам не нужен оператор new
. Вы можете просто написать var F = Function (); чтобы получить тот же результат.

Причуды
В документации MDN есть несколько хороших примеров причуд, в том числе тот факт, что функции, объявленные с помощью конструктора Function, не наследуют свою текущую область видимости должным образом (т. е. закрытие не сформирован).

Это означает, что у них нет доступа к переменным в их охватывающей области видимости, что не особенно полезно:

function foo() {
  var bar = 'blah';
  var first = new Function('console.log(typeof bar)');
  
  first();   // undefined
  
  var second = function(){
    console.log(typeof bar);
  }
  second();  // string
}
foo();

В функции first мы используем конструктор Function, поэтому у него нет доступа к панели переменных. Однако, если мы используем функцию «second», которая является функциональным выражением, она действительно имеет доступ к переменным, определенным в его охватывающей области (через закрытие).

Другими словами, не используйте конструктор функций.

Частный случай - конструктор объекта

В этом случае мы на самом деле не определяем функцию, хотя мы используем ключевое слово function.

var G = new function() { ... };

new function () {}; создает новый объект и вызывает анонимную функцию в качестве его конструктора. Если объект возвращается из функции, он становится результирующим объектом, в противном случае новый объект создается с нуля, и функция выполняется в контексте этой новой функции.

Несколько необычно видеть это в таком виде. Давайте сделаем это как следует:

var Person = function(){
  console.log(this);  // Person
}
var joe = new Person();

Итак, на самом деле с оператором new мы даем ему новый контекст «this», а затем выполняем данную функцию с этим новым контекстом. Сильно отличается от определений функций, с которыми мы имели дело выше.

Стрелочные функции

С добавлением ES6 появились так называемые функции «толстой стрелки» или, если вы предпочитаете уменьшенную версию, просто «функции стрелок».

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

Стрелочные функции особенно удобны в тех случаях, когда раньше у нас были однострочные функции, как в этом случае с ES5 JavaScript:

Давайте посмотрим на пример:

[1, 2, 3].map(function(x) {return x * 2});
// [2, 4, 6]

В приведенном выше случае теперь мы можем написать, как показано ниже:

[1, 2, 3].map(x => x * 2);
// [2, 4, 6]

Второе преимущество стрелочных функций заключается в том, что они сохраняют свой контекст this, что очень удобно. Часто попытка сохранить область видимости в JavaScript является утомительной задачей, обычно с помощью bind (). Но это позволяет обойти это и меньше писать.

Возьмем другой пример:

function multiply(array) {
  this.multiplier = 2;
  return array.map(function(x) {return x * this.multiplier});
};

var fakeContext = {};

multiply.call(fakeContext, [1, 2, 3]);
//[NaN, NaN, NaN]

Это не сработало, потому что контекст this функции умножения не был сохранен в map () функция, вынуждая нас немного переписать, чтобы сохранить контекст функции, путем определения переменной в multiple () (например, var self = this ) или с помощью bind ().

Вместо этого, используя стрелочную функцию, мы знаем, что наш контекст this будет сохранен, что позволяет нам писать с небольшой настройкой:

function multiply(valuesArray) {
  this.multiplier = 2;
  return valuesArray.map(x => x * this.multiplier);
};

var fakeContext = {};

multiply.call(fakeContext, [1, 2, 3]);
// [2, 4, 6]

В этой статье ES6 Arrow Functions есть подробное объяснение.

Резюме

Функции - это основные строительные блоки скриптов.

  • Определения функций подняты, выражения - нет.
  • Функции выполняются при их вызове. Это называется вызовом функции.
  • Значения можно передавать в функции и использовать внутри функции. Имя значения называется параметром. Само фактическое значение называется аргументом.
  • Функции всегда return значение. В JavaScript, если значение return не указано, функция по умолчанию вернет undefined.
  • Функции - это объекты.