DEV Community

Cover image for JavaScript Functions: a breakdown.

JavaScript Functions: a breakdown.

Welcome back Fam!

Today I wanted to create a blog to break down the different types of JavaScript functions in the most basic terms.

When I was first starting to code the MDN docs confused me most of the time, so this will be part of an ongoing series into the core fundamentals of JavaScript written by someone who didn't come from a CompSci background.


What is a Function

That's right, we need to start from the beginning!

A function is the very building block of coding, and whether it is JavaScript or Java you're going to want to get acquainted with the fundamentals of the fundamentals.

In JavaScript (like most languages) a function is a block of code that performs a task or returns a value, one good thing to note at this point is a function should do that, perform one function (but that's a debatable story for another time).

But to make it a bit more interesting, JavaScript has evolved and has quite a few different ways to declare and use them, so let's start.


Function Declaration

I will start with a Function Declaration because it comes first, and I mean that because a Function Declaration gets hoisted the moment the program is compiled.

Hoisting you ask? hoisting is the way JavaScript allocates functions to memory and in what order, and this is a topic worth exploring on its own and this article explains it well.

function funcName(withParams) {
  // params can be declared, but are not enforced;
};

function funcName() {
  // without declared params;
};

funcName();
Enter fullscreen mode Exit fullscreen mode

Syntax:

  • starts with 'function'
  • followed by the name
  • with optional params, if you don't declare params, you can still pass them though and they'll be accessed with arguments
  • invoked by calling the function name

The reason a Function Declaration gets hoisted first is that as it compiles, JavaScript looks for the declarations starting with 'function', makes sense, huh, this then hoists all those declarations to the top of the pile.

This can lead to some gotcha moments though that we should be aware of, mainly that if a program is compiled and a Function Declaration gets invoked before the Function Declaration is hoisted.


Function Expressions

A Function Expressions is where an anonymous function is assigned to a variable(var/let/const)

const funcName = function() {
  // do something;
};

funcName();
Enter fullscreen mode Exit fullscreen mode

Syntax:

  • starts with the name declared in a variable
  • function() assigned to the variable
  • params operate the same way
  • invoked the same way

Function Expressions are not hoisted, so this would prevent those icky instances of a Function Expressions getting invoked before it gets hoisted and stashed in memory.

P.S. while Function Expressions are not hoisted the VAR declaration IS, so if you declare a function with VAR it will be hoisted.

But wait,

Yoda Quote 'there is another'

And it's called a Named Function Expression

const funcName = function yoda(){
  // do something
};

typeof yoda; // -> "undefined"
funcName(); // -> "function"
Enter fullscreen mode Exit fullscreen mode

So why do this right? as Yoda is 'undefined' when we checked the type, the bonus with a named function expression is the name is recognised within the function itself and can help dramatically when debugging as the name will show up in your call stack, whereas an anonymous function wouldn't show up in the stack.

const funcName = function yoda(param) {
  console.log(typeof yoda === 'function'); // -> true
  return typeof param;
}

funcName(3);     // -> 'number'
funcName.name;   // -> 'yoda'
Enter fullscreen mode Exit fullscreen mode

So as you level-up it might be worth considering if Named Function Expressions are for you?


Arrow Functions

Ahh Arrow Functions, everyone's new favourite addition to ES6.

Arrow Functions are a Syntactic extension of Function Expressions, utilising what we call a fat arrow (or I prefer to call a rocket), and it can be constructed in a few different ways.

// -> no params
const funcName = () => {
  // do something;
};

// -> one param
const funcName = param => {
  // if one param the brackets arent needed;
};

// -> more than one param
const funcName = (paramOne, paramTwo, paramThree) => {
  // but if more than one they are required;
};

// -> if the function is simple enough it can be simplified to a single line, removing the need for brackets after the rocket.
const funcName = (x, y) => x * y;
Enter fullscreen mode Exit fullscreen mode

Like with all our functions so far there are some times when Arrow Functions, so let's go over a few of these gotchas.

It gets funky with This

Wes Bos can probably do a way better at explaining this, with this.
As he does such a good job I'll phone a friend and leave you to review that article rather than repeat.

Constructors

Arrows Functions can't be used as constructors, therefore 'New' cant be invoked with an Arrow Function.

Object literals

And the final sticky part ill cover is object literals, due to the syntax Arrow Functions can't decipher them, for example.

An ES5 object literal would operate like this.

const setColour = function (colour) {
    return {value: colour}
};

let backgroundColour = setColour('Blue');
backgroundColour.value; // -> "Blue"
Enter fullscreen mode Exit fullscreen mode

But since the Arrow Functions use the curly braces by default, they can't differentiate between a block scope and an object literal and would result in an error.

const setColour = colour => {value: colour };
Enter fullscreen mode Exit fullscreen mode

But this can be overcome but wrapping them as such.

const setColour = colour => ({value: colour });
Enter fullscreen mode Exit fullscreen mode

Immediately Invoked Function Expressions(IIFE)

As the name does suggest, this is a function that is invoked the moment it is defined.

(function () {
    // do something;
})();
Enter fullscreen mode Exit fullscreen mode

This one looks a bit weirder, but it is just an anonymous function wrapped in brackets and invoked immediately after.

They can be handy as they're invoked immediately, and not hoisted into the global scope (and this could be considered pollution), they cannot be expressed with a Rocket.

As they are invoked due to the compile process, they do not need a name, but they can be given one.

(function funcName() {
  // do something;
})();
Enter fullscreen mode Exit fullscreen mode

There are plenty of bonus features with IIFE but I will point you to an amazing article that covers all those quite clearly.


Well, I hope you liked the article if you reached this far a like or a share would be lovely if you were so inclined.

I will be back soon with some more JavaScript-related content.

Much luv
Coops

Top comments (3)

Collapse
 
pentacular profile image
pentacular

In this example, funcName is being invoked BEFORE the function is declared, and will result in an error saying the function is not defined.

This works just fine for me, and I rather thought this was the point of hoisting.

funcName();
function funcName() {
  // do something;
}

My understanding is that with hoisting, the above example is re-arranged to the below form.

function funcName() {
  // do something;
}
funcName();

Which is why it does not produce an error.

Function Expressions are not hoisted, so this would prevent those icky instances of a Function Expressions getting invoked before it gets hoisted and stashed in memory.

Perhaps not, but var declarations are, so ...

funcName();
var funcName = function ()  { /* do something */ };

is re-arranged to

var funcName;
funcName();
funcName = function () { /* do something */ };

Which does have the problem you described above, since var declarations are hoisted, but not the initializations, and we get something like "TypeError: funcName is not a function".

Arrow Functions are a Syntactic extension of Function Expressions

I'm not entirely sure you can reasonably call them a syntactic extension, since they have different semantics.

But since the Arrow Functions use the curly braces by default, they can't differentiate between a block scope and an object literal and would result in an error.

Well, your example won't result in an error, but it may be surprising when it always returns undefined.

const setColour = colour => {value: colour };

console.log(setColour('blue'));

value is a perfectly good label, and colour is a perfectly good expression, after all.

The moral of the story being that it's always a good idea to test your examples to see if they actually do what you expect. :)

Collapse
 
coopercodes profile image
🧸 🏳️‍🌈 cooper-codes 💻 🎮

Thanks for the feedback, I’ll review this tomorrow morning and edit

Collapse
 
pentacular profile image
pentacular

You're welcome. :)