logo

How are regular functions and function expressions different in JavaScript?

I’ve been asked this question countless times during mentorship meetings, meetup networking sessions and forums.

– What is the difference between assigning an anonymous function to a variable and a regular function?

To know this difference the only requirement is to know how hoisting works in JavaScript.

TL;DR – Hoisting

The main difference between a function expression and a regular function is the following:

Regular functions are hoisted to the top of the scope, with their full body whilst function expressions are hoisted like regular variables.

Here is an example to outline the process:

// Your code
function scopeFunc() {
  var a = 5;
  function fn1() {
    fn2();
    console.log('fn1()');
  }

  fn1();

  var fn2 = function() {
    console.log('fn2()');
  }
}

// After hoisting
function scopeFunc() {
  function fn1() {
    fn2();
    console.log('fn1()');
  }

  var a;
  var fn2;

  fn1();
  fn2 = function() {
    console.log('fn2()');
  }
}

If you run scopeFunc you will get the following result:

> scopeFunc()
> TypeError: fn2 is not a function

The longer story

In JavaScript, variables and functions within a scope are hoisted to the top. The nuance is that everything is hoisted based on the way it was declared / defined.

Take the following example:

function printSum() {
  var a = 5;
  var b = 7;

  function sum(x, y) {
    return x+y;
  }

  var message = 'sum = ';

  function print(string) {
    console.log(string);
  }

  print(message + sum(a,b));
}

printSum();

When parsed, the code will end up looking something like this:

function printSum() {
  function sum(x, y) {
    return x+y;
  }

  function print(string) {
    console.log(string);
  }

  var a;
  var b;
  var message;


  a = 5;
  b = 7;
  message = 'sum = ';


  print(message + sum(a,b));
}

Running the printSum function will have the following output.

> printSum();
> "sum = 12"

It is clear that all variable declarations have been “hoisted” to the top of the scope. Not only that but the functions have been hoisted as well.

Unlike regular variables, functions get hoisted to the top of the scope, with their body.

If you ever wondered why you can call a function before it has been defined, this is the reason.

function scopeFunc() {
  function fn1() {
    fn2();
    console.log('fn1()');
  }

  fn1(); // Execute first function which in turn, calls second function

  function fn2() {
    console.log('fn2()');
  }
}

scopeFunc(); // Execute wrapping function

The reason you can call fn2 inside fn1 even before it has been defined is hoisting. I’m guessing that the engine hoists functions based on their call site.

If the engine had a simple top-down approach, with no prioritization, fn1 would have been hoisted before fn2 and a ReferenceError would have been thrown.

As you can see, we are able to call fn2 which makes me believe that the engine either keeps track of where the function is being used, either it hoists whatever it finds, in reverse order.

I hope this clarifies the main difference between function expressions and regular functions in JavaScript. Do use the comments section or ask questions on Twitter if you have any suggestions.

Reference

Photo credits: Sean MacEnteeCranes

Copyright (c) 2023 Adrian Oprea. All rights reserved.