Hack Frontend Community

Differences Between Arrow Function, Function Declaration and Function Expression

In JavaScript there are several ways to declare functions: Function Declaration, Function Expression and Arrow Function. Each has its own features regarding hoisting, this usage, ability to be a constructor, and nuances in syntax and application.

Brief Comparison Table

CharacteristicFunction DeclarationFunction ExpressionArrow Function
Syntaxfunction name() { ... }const name = function() { ... }const name = () => { ... }
HoistingFull (function accessible before declaration)Partial (variable hoisted but function initialized only at declaration point)No (when declared with const/let function doesn't hoist)
Own thisYes (depends on call)Yes (depends on call)No (lexical this, inherited from outer scope)
argumentsAvailableAvailableNo (use ...rest)
Use with newYes (can act as constructor)Yes (if declared as regular function)No (Arrow Function can't be constructor)
ApplicationGeneral functions, constructorsClosures, callbacksOne-liners, callbacks

Details and Examples

Hoisting

  • Function Declaration fully hoists. This means we can call such function even before its actual declaration in file.

    hello(); // "Hello from Declaration!"
    
    function hello() {
      console.log("Hello from Declaration!");
    }
    
  • Function Expression with var hoists as variable (becomes undefined before initialization), but function itself becomes available only after assignment line. If using const or let, no hoisting for function as ready value — you can't call function before its declaration.

    // Example with var
    console.log(typeof sayHi); // "undefined"
    // sayHi(); // Error: sayHi is not a function
    
    var sayHi = function () {
      console.log("Hello from Expression!");
    };
    
  • Arrow Function usually declared with const or let, so doesn't hoist as ready function. If trying to call arrow function before declaration, will get error.

    // greet(); // Error: greet is not defined
    
    const greet = () => console.log("Hello from Arrow!");
    

this Context

  • Function Declaration and Function Expression (if not arrow) have this context determined by call method (or .call, .apply, .bind binding).

  • Arrow Function doesn't have own this: it's lexically borrowed from scope where arrow function is declared.

    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 taken from outer scope (global / undefined)
    

arguments and ...rest Operator

  • Function Declaration and Function Expression have pseudo-array arguments containing all function arguments.

  • Arrow Function doesn't have arguments, but you can use ...rest operator to get all arguments.

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

Using with new

  • Only Function Declaration and Function Expression (not in arrow format) can be constructors.
  • Arrow Function can't be used with new as it doesn't have internal [[Construct]] mechanism.