Функции

Псевдомассив arguments

Рассмотрим простейшую функцию для определения суммы чисел:

function fn(a, b) {
  return a + b;
}

fn(2, 4)  // 6

Предположим, нам нужно передать в функцию неизвестное количество аргументов.
Когда функция запускается, внутри неё создаётся переменная arguments. Переменная arguments доступна исключительно внутри функции. Она содержит псевдомассив аргументов.

function fn() {
  return arguments;
}

fn(2, 4, 10, 7) // [2, 4, 10, 7]

В arguments всегда находится актуальное состояние аргументов. Убедимся в этом:

function fn() {
  var arr = [];
  for (let i = 0; i < arguments.length; i++) {
    arr.push(arguments[i]);
  }
  return arr;
}

fn(2, 4, 10, 7) // [2, 4, 10, 7]

Хоть  arguments похож на масив. и браузер его показывает как массив, на самом деле это объект с числовыми ключами. То, что консоль браузера выводит как [2, 4, 10, 7] на самом деле больше похоже на { 0: 2, 1: 4, 2: 10, 3: 7 }. Поэтому методы массивов к arguments применимы не всегда.
Вот такая запись выведет ошибку

function fn() {
  return arguments.replace((a, b) => a + b);
}

fn(2, 4, 10, 7)  // TypeError: arguments.replace is not a function

Суммировать все аргументы можно обычным перебором

function fn() {
  var sum = 0;
  for (let i = 0; i < arguments.length; i++) {
    sum += arguments[i];
  }
  return sum;
}
fn(2, 4, 10, 7)   //   23

Типы объявления функций


Function Declaration - функции объявляются в основном коде и обрабатываются интерпретатором до выполнения кода. Пример:

function fn1(a, b) {
  return a + b;
}

Function Expression - функции объявляются в контексте выражения, например, присваивания, обрабатываются в основном потоке кода:

var fn2 = function fn2(a, b) {
  return a + b;
}

Само слово Expression переводится как "выражение".

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


Принятый в 2015 году ES 6 принёс много упрощений, в том числе и в способе записи функций.
Для примера возьмём обычную функцию и посмотрим как её можно записать по стандартам ES 6.
Начинаем с  Function Expression:

var fn2 = function fn2(a, b){
  return a + b;
}

Убираем ключевое слово  function, убираем имя функции fn2, и после круглых скобок перед фигурными ставим стрелку.
Результат:

var fn2 = (a, b) => {
  return a + b;
}

Если внутри стрелочной функции только одно выражение, как в данном случае, можно убрать фигурные скобки и ключевое слово return.

var fn2 = (a, b) => a + b;

fn2(2, 3)   //   5

Стрелочная функция это всегда  Function Expression.

Тоже стрелочная функция. И тоже Function Expression:

var a = [2, 3, 4];
a.map(n => n*n);   /
[4, 9, 16]

Полезный совет. Стрелочные функции — одна из возможностей JavaScript. И, как и любые другие возможности, использовать их следует там, где это уместно. Ближайшая аналогия - лексика любого языка, которая имеет множество слов и оборотов. Но никто не станет использовать их все только потому, что они существуют. Мы выбираем нужные слова в зависимости от ситуации. Так и со стрелочными функциями — использовать их нужно тогда, когда они улучшат код и не использовать там, где они ухудшат читаемость.

Замыкания


Замыкания — способность функции запоминать переменные, которые были доступны в той области видимости, в которой функция была объявлена.

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

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

Каррирование


Рассмотрим функцию:

function filter(arr, someFn) {
  var res = [];
  for(var i = 0; i < arr.length; i++) {
    if(someFn(arr[i]) === true) {
      res.push(arr[i]);
    }   
  }
  return res;
}

var arr = [1, 2, 3, 4, 5];

function greaterThanTwo(a) {
  return a > 2;
}

filter(arr,
greaterThanTwo)  // [3, 4, 5]

Что здесь происходит.
Функция filter проверяет каждый элемент массива arr на то, соответствует ли он условиям некоей функции someFn и, если соответствует, добавляет этот элемент в массив res, который данная функция возвращает.
Функция greaterThanTwo указывает условие a > 2, то есть фильтруются все элементы массива, значение которых больше, чем два.

Отлично. Теперь представим, что у нас изменилось условие, и мы ищем только те элементы, значение которых больше 3, 4 и т.д. Для каждого такого случая можно написать отдельную функцию-условие, и часто так и делают, но можно сделать и по-другому.
Перепишем функцию-условие следующим образом:

function greaterThan(than) {
  return function(number) {
    return number > than;
  }
}


Это тоже пример использования замыкания. Анонимная функция function(number) ищет переменную than в своей локальной области видимости, не находит, поднимается вверх, ищет переменную than в той области видимости, в которой была создана, то есть в локальной области видимости функции greaterThan, находит и использует.


Теперь запускать функцию filter можно следующим образом

filter(arr, greaterThan(2))

В качестве аргумента функции greaterThan(than) мы можем указать любое нужное нам число. Теперь нам не нужно дублировать функцию-условие и мы можем использовать её повторно с нужными аргументами

var res1 = filter(arr, greaterThan(3));   // [4, 5]
var res2 = filter(arr, greaterThan(4));   // [5]

Каррирование — побочный эффект замыкания, когда мы вызываем функцию внутри функции в которой переменные привязаны к определённым значениям.

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

Анонимные функции


Анонимные функции — это функции без имени. Используются в тех случаях, когда имя функции не важно. Например, как в коде выше, когда функция возвращается из функции. Или когда небольшая функция, вместе с её телом, используется в качестве параметра другой функции. То есть в данных случаях идёт речь о Function Expression. Если функция объявляется как Function Declaration, имя функции является обязательным.

 
Особый случай использования анонимных функций IIFE (Immediately Invoked Function Expression) это JavaScript функция, которая выполняется сразу же после того, как она была определена.
Для такого вызова тело функции берут в круглые скобки и после них ставят ещё круглые скобки. Выглядит это примерно так:

(function (a, b) {
  return a + b;
})(2, 3)   // 5


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

Всплытие или hoisting


Подробно здесь
Кратко: до начала выполнения кода интерпретатор  загружает в память функции, объявленные как Function Declaration вместе с телом функции и имена, но не значения, переменных, объявленных через var. То есть ещё до начала выполнения кода уже известно какие там будут Function Declaration и какие имена переменных.