Asynchronous JavaScript with async/await

In this course we will learn how to use the ES2017 async and await keywords to write asynchronous code that is more readable and easier to follow than equivalent code based on long promise chains or deeply nested callbacks.

Watch the Course

Function Definitions in JavaScript

JavaScript has multiples ways of defining a function. There are function declarations, function expressions, and (since ECMAScript 2015) arrow functions. All of the former can be used to define a function. The three kinds differ in their syntax and their rules for naming and hoisting as explained below.

Function Declarations

A function declaration is a statement. It can therefore only appear in statement position. This means that if you see a function definition in a place where only expressions are allowed, you're not looking at a function declaration, but a function expression.

Each function declaration starts with the function keyword and must always have a name. Without a name, there would be no way to refer to the function that was defined. Here's a simple example of a function declaration:

function add(a, b) {
    return a + b;
}

Functions defined in a function declaration are hoisted, which means that you can use the function although it's defined below the code using it. Hoisted functions are made available everywhere within the current scope:

var x = add(1, 2);
// x = 3

function add(a, b) {
    return a + b;
}

Function Expressions

As the name suggests, a function expression is not a statement. It can appear everywhere an expression is expected, for instance on the right-hand side of a variable assignment:

var add = function(a, b) {
    return a + b;
};

Notice the semicolon after the closing brace. The function is just a value like any other expression, and thus the assignment expression is terminated by an explicit semicolon.

Function expressions can be named, but they don't have to be. The above add function, for example, isn't named. It is assigned to the add variable, but it doesn't have a name itself. We could give the function a name, which makes the definition syntax look confusingly similar to the function declaration syntax:

var add = function add(a, b) {
    return a + b;
};

Now we have what's called a named function expression. The function name makes debugging a lot easier because in case of an error, the call stack can now show descriptive function names instead of the not-so-helpful (anonymous function).

Don't confuse the left add (the variable identifier) with the right add (the function name identifier), though. They are completely independent and can be different:

var someVariable = function add(a, b) {
    return a + b;
};

A named function expression also enables the function to recursively call itself. The function name identifier, however, is only available within the function itself. It is not visible within the enclosing scope.

As opposed to function declarations, function expressions are not hoisted and can therefore not be used before they're defined. The variable identifier add is hoisted, but it has the value undefined until the function is assigned to it. This leads to an error when attempting to call add before properly initialized:

var x = add(1, 2);
// TypeError: add is not a function

var add = function add(a, b) {
    return a + b;
};

Arrow Functions

With ECMAScript 2015, JavaScript got arrow functions, which are mainly syntactic sugar for defining function expressions. Here's how the arrow function version of the add function looks like:

var add = (a, b) => a + b;

Arrow functions mostly behave like function expressions. They are expressions rather than statements, which allows them to appear in expression position. The brevity of the arrow function syntax is extremely helpful for small functions passed to a higher-order function such as map:

const numbers = [1, 2, 3];
const doubled = numbers.map(n => 2 * n);

Just like functions expressions, arrow functions aren't hoisted — only function declarations are. There are two main differences between arrow functions and function expressions, though:

  1. Arrow functions cannot be named. The arrow function syntax is very concise and meant for simple, brief functions. It doesn't allow for a function name.
  2. Arrow functions lexically bind the current this value. The treatment of this within an arrow function is different than within non-arrow function expressions. ES In Depth: Arrow functions is a great explanation of the different behavior.

The Language Grammar

For those of you interested in the ECMAScript grammar, the following sections of the language specification describe the syntax and semantics of function definitions:

Learn Node