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
| Characteristic | Function Declaration | Function Expression | Arrow Function |
|---|---|---|---|
| Syntax | function name() { ... } | const name = function() { ... } | const name = () => { ... } |
| Hoisting | Full (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 this | Yes (depends on call) | Yes (depends on call) | No (lexical this, inherited from outer scope) |
arguments | Available | Available | No (use ...rest) |
Use with new | Yes (can act as constructor) | Yes (if declared as regular function) | No (Arrow Function can't be constructor) |
| Application | General functions, constructors | Closures, callbacks | One-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
varhoists 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
constorlet, 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
thiscontext 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
argumentscontaining all function arguments. -
Arrow Function doesn't have
arguments, but you can use...restoperator 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
newas it doesn't have internal[[Construct]]mechanism.