Весной стартует сезон найма, успей отхватить свой оффер!

Различия между Arrow Function, Function Declaration и Function Expression

В JavaScript существует несколько способов объявлять функции: Function Declaration, Function Expression и Arrow Function. Каждый из них имеет свои особенности в плане hoisting, использования this, возможности быть конструктором, а также нюансов в синтаксисе и области применения.

Краткое сравнение в таблице

ХарактеристикаFunction DeclarationFunction ExpressionArrow Function
Синтаксисfunction name() { ... }const name = function() { ... }const name = () => { ... }
HoistingПолное (функция доступна до объявления)Частичное (переменная всплывает, но функция инициализируется только в точке объявления)Нет (при объявлении через const/let функция не всплывает)
Собственный thisДа (зависит от вызова)Да (зависит от вызова)Нет (лексический this, наследуется из внешней области)
argumentsДоступенДоступенНет (использовать ...rest)
Использование с newДа (может выступать как конструктор)Да (если объявлена как обычная функция)Нет (Arrow Function не может быть конструктором)
ПрименениеОбщие функции, конструкторыЗамыкания, колбэкиОднострочные функции, колбэки

Детали и примеры

Hoisting (всплытие)

  • Function Declaration полностью всплывает. Это значит, что мы можем вызвать такую функцию даже до места её реального объявления в файле.

    hello(); // "Hello from Declaration!"
    
    function hello() {
      console.log("Hello from Declaration!");
    }
    
  • Function Expression c var всплывает как переменная (становится undefined до инициализации), но сама функция становится доступна только после строчки с присвоением. Если используется const или let, то никакого hoisting для функции, как готового значения, не происходит — вы не сможете вызвать функцию до её объявления.

    // Пример с var
    console.log(typeof sayHi); // "undefined"
    // sayHi(); // Ошибка: sayHi is not a function
    
    var sayHi = function () {
      console.log("Hello from Expression!");
    };
    
  • Arrow Function объявляется обычно через const или let, и поэтому не всплывает как готовая функция. Если попробовать вызвать стрелочную функцию до её объявления, будет ошибка.

    // greet(); // Ошибка: greet is not defined
    
    const greet = () => console.log("Hello from Arrow!");
    

Контекст this

  • У Function Declaration и Function Expression (если это не arrow) контекст this определяется способом вызова (или привязкой .call, .apply, .bind).

  • У Arrow Function нет собственного this: он лексически заимствуется из области, в которой объявлена стрелочная функция.

    const user = {
      name: "Alice",
      sayHello: function() {
        console.log("Hello, I'm " + this.name);
      },
      arrowHello: () => {
        console.log("Hello, I'm " + this.name);
      }
    };
    
    user.sayHello();    // "Hello, I'm Alice" - this = user
    user.arrowHello();  // "Hello, I'm undefined" - this берётся из внешней области (глобальная / undefined)
    

arguments и оператор ...rest

  • Function Declaration и Function Expression имеют псевдомассив arguments, содержащий все аргументы функции.

  • Arrow Function не имеет arguments, но можно использовать оператор ...rest для получения всех аргументов.

    function sumAll() {
      let total = 0;
      for (const arg of arguments) {
        total += arg;
      }
      return total;
    }
    
    const sumAllArrow = (...args) => {
      return args.reduce((acc, curr) => acc + curr, 0);
    };
    

Использование с new

  • Только Function Declaration и Function Expression (не в формате стрелки) могут быть конструкторами.
  • Arrow Function не может использоваться с new, так как у неё нет внутреннего механизма [[Construct]].