Skip to main content

Ways to declare functions

In JavaScript a function is a value, so the language offers several syntactic forms for creating one, from the classic function to arrows, generators and class methods. The forms differ in more than looks: the choice decides hoisting, how this behaves, whether prototype exists and whether the function can be called with new.

Theory

TL;DR

  • A function declaration is hoisted, has its own this and can be a constructor.
  • A function expression is available only after the assignment, and can be anonymous or named.
  • An arrow function takes this lexically, has no arguments, no prototype and does not work with new.
  • Special kinds: new Function (built from a string, not recommended), function* (generator), async function (returns a Promise).
  • Object and class methods are functions too, but with super support through [[HomeObject]].
  • IIFE and bind() are not new syntactic forms, they are ways to run a function immediately or to fix its this.

Quick example

javascript
function sum(a, b) { // 1. function declaration return a + b; } const mul = function (a, b) { // 2. function expression return a * b; }; const inc = x => x + 1; // 3. arrow function const obj = { sayHi() { console.log('hi'); } // 4. method shorthand };

Classic forms: declaration, expression, arrow

1) Function declaration

javascript
function sum(a, b) { return a + b; }
  • It is hoisted: the function can be called before its declaration in the code.
  • It has its own this when called as a function or as a method.
  • It can be used as a constructor (new Sum()).

2) Function expression

javascript
const mul = function (a, b) { return a * b; };
  • No hoisting: the function is available only after the assignment.
  • It can be anonymous or named:
javascript
const fact = function factorial(n) { /* ... */ }; // named: the name is visible inside

The name of a named expression is visible only inside the function itself, which is handy for recursion and for a readable stack trace.

3) Arrow function

javascript
const inc = x => x + 1; const sum = (a, b) => a + b;
  • Lexical this: it is taken from the outer lexical environment.
  • No arguments (use rest parameters ...args instead).
  • Cannot be used with new, has no prototype.
  • A great fit for callbacks and short functions.

Special kinds: Function, generators, async

4) Function constructor

javascript
const fn = new Function('a', 'b', 'return a + b');
  • Creates a function from a string, just like eval.
  • Not recommended: security and performance problems, plus the body is compiled in the global scope.

5) Generator function (function*)

javascript
function* idGenerator() { let i = 0; while (true) { yield i++; } }
  • Returns an iterator and supports yield.
  • Execution can be paused and resumed.

6) Async function

javascript
async function fetchData() { const r = await fetch('/api'); return r.json(); } const fn = async () => { /* an async arrow works too */ };
  • Always returns a Promise.
  • You can use await inside it.

Combinations are allowed too: async function* is an async generator for for await...of.

Object and class methods

7) Method shorthand in objects

javascript
const obj = { sayHi() { console.log('hi'); }, // method async load() { await fetch('/'); } // async method };
  • Convenient for methods: they have [[HomeObject]], which means super works.
  • You can also write prop: function () {}, but that is a plain function expression with no super support.

8) Class methods (inside class)

javascript
class C { method() {} static staticMethod() {} async asyncMethod() {} }
  • A class declaration is not hoisted the way a function declaration is: before the class line runs, the class cannot be used.
  • Methods are used with instances or with the class itself and they support super.
  • A class body always runs in strict mode.

Derived forms: IIFE and bind

9) IIFE, Immediately Invoked Function Expression

javascript
(function () { // runs immediately })();
  • Useful for creating a local scope; before modules existed this was the main way to keep the global scope clean.

10) Function.prototype.bind (creating a bound function)

javascript
function f() { console.log(this); } const bound = f.bind({ x: 1 });
  • Returns a new function with a fixed this and, optionally, with partially applied arguments.
  • Binding an already bound function again does not change the context.

Common mistakes

  • Relying on hoisting for a function expression: calling mul(2, 3) above const mul = function ... throws a ReferenceError.
  • Declaring an object method as an arrow and expecting this to point at that object.
  • Using new Function or a string instead of a normal function: it carries the same risk as eval.
  • Forgetting that an async function returns a Promise and reading its result without await or .then().
  • Confusing prop: function () {} with the shorthand prop() {}: the first has no [[HomeObject]], so super does not work in it.
  • Declaring a function declaration inside an if block and expecting consistent behaviour across environments: use an expression instead.

Comparing the forms

FormHoistingOwn thisnewprototype
Function declarationYesYesYesYes
Function expressionNoYesYesYes
Arrow functionNoNo (lexical)NoNo
new FunctionNoYesYesYes
Generator function*Yes (for a declaration)YesNoYes
Async functionYes (for a declaration)YesNoNo
Object or class methodNoYesNoNo

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.