Custom JavaScript Functions

Marijn Wijbenga
The Startup
Published in
11 min readSep 23, 2020

--

A tutorial about the intricacies of custom JavaScript functions and the different ways of declaring and using them.

Disclaimer
I am writing about JavaScript related topics to demystify them for myself and hopefully for future readers. If you see mistakes, please point them out in the comment section, I’m always eager to learn and will keep editing this tutorial to contain the best possible explanations.

Table of contents

In JavaScript you can create functions to write reusable blocks of code with functionality similar to most other programming languages, although, because of the nature of JavaScript, they might behave a bit differently, depending on the circumstances and the way you declare them.

Function declaration

In its simplest form a custom JavaScript function is declared as follows.

You can then call (use) this function later in your code with:

Now as it is, our function doesn’t actually do anything. So let’s add some functionality and delve into the subject of arguments and parameters.

Arguments and parameters

The functionality we’ve added here is simply logging to the console the result of number1 + number2. As you can see these variables aren’t declared or defined anywhere else but between the function parenthesis. This is what are called parameters.

Parameters are variables entered as a part of the function definition. And these are the types of values we expect to be inserted when our function is called, like so:

The numbers 1 and 2 are passed into the parenthesis of the function when it is called, and are called arguments.

So what happens is this:

Now the weird, but probably useful thing about arguments is, you can actually enter more arguments than parameters you’ve declared.

Doing something like:

is actually valid, but as of yet it doesn’t do anything with these extra arguments. All arguments are stored inside an array-like object called argumentsso you could iterate through them with a for loop. Array-like because it is not actually an array, so using methods like arguments.forEach() won’t work.

Since the advent of JavaScript ES6 there is a new way to access the remaining arguments, by entering a variable name preceded by 3 points, like …restArgs to the end of your parameter list at the function declaration, which can then be used to access these excess arguments, which are stored in an actual array, so array methods can be used on them.

Or you can simply store all arguments inside an allArgsarray like so:

There are other ways of converting the array-like object of arguments into an actual array, but these fall outside of the scope of this tutorial, so we won’t be mentioning them.

Default parameters and undefined

When we entered our parameters between the function parenthesis (the function head) we didn't actually define the value of any of these parameters, but this is possible with predefined or default parameters. You set these up like this:

You can then fall back to this default value by entering undefinedas an argument:

Up until now we just logged the data from the function with console.log. But there is a method to export or returndata out of your functions, which is the subject of our next topic.

Return

In JavaScript, if no return value is specified, the function will return undefined.

What happens here is simply the result of number1+number2 being exported out of the scope of the function. When we call the function there isn’t actually anything being outputted to the console but the result is there, and can be stored into a variable to be used to do something else.

An important thing to remember is that return, upon being called, also ends the function block it is called in. So code that is placed after the return statement is never reached and therefore not executed. Another thing worth remembering is that returncan only be used inside a function.

Return can also be used to end execution of that block of code, like:

To return multiple values from a function you can use curly braces to store the returned values inside an object:

Another thing to note about return is that when you have a function that is inside another function, using return in the child function, will only end the child function, not the parent function.

We already briefly mentioned scope, and scope in JavaScript being the fickle mistress she is, now would be a good time to briefly have a look at the inner of her workings.

Scope

Scope answers the questions: where in the document are my functions and variables available to me. In JavaScript, there’s a global scope, a function scope and a block scope. Let’s look into these one by one.

Global Scope

When referring to the global scope, what we are actually talking about is the windowobject in the DOM. The windowobject is the top level object in a browser, and can be seen as the root of the document object model.

Functions and var variables, declared inside the global scope, are available inside the global scope and attached to the window object.

However, constand letare not attached to the window object, but are always block scoped, even if you define a constor letinside the global scope.

Function scope

When a var, letor constare created inside a function, they are only available inside the function.

The same goes for using letor const, in the above scenario.

Block scope

Whenever you put something inside curly braces { }, a block scope is created, and variables (var, letor const) declared inside of those curly braces, are only available inside that block. The same goes for the head of the block, what’s between the parenthesis.

Key points to remember:

  • vardefined in global: globally scoped and attached to the .window object.
  • vardefined in function/block: function/block scoped and only available inside the function.
  • let & const: block scoped, and unavailable to the .window object.
  • Scope lookup chain: JavaScript looks up for your variables, meaning, if it can’t find it in the current scope, it will go up one scope and look there, and so on, until it finds it or returns an undeclared.

Another topic important to understanding how JavaScript functions work, is hoisting, the subject of our next topic.

Hoisting

Hoisting, literally meaning to pull something up (like hoisting the flag, the sail, etc.), refers to the mechanism JavaScript pulls behind the curtains to put functions and variable declarations up to the top of their respective scope before execution.

Surprisingly, this

works, and is the result of hoisting. Why does JavaScript do this?

Hoisting is the result of how the JavaScript interpreter handles the code. It works in two cycles:

  1. Interpreter looks for function and variable declarations (not the actual value assignments) and move these to the top of their respective scope.
  2. Interpreter executes the code like you would expect normally.

So the following piece of code:

Is actually being executed/interpreted as

U can therefore, call your function before having declared it:

Strict mode

There is another strange type of behaviour concerning hoisting, that is the result of not declaring your variables but instead assigning a value to it like so:

Since message is not being declared as a var let or const, the JavaScript interpreter does this for you in the background and turns it into a global scope var. This could lead to some unpredicted behaviour so declaring your variables is probably always a good design practice. However, to prevent this type of behaviour, you can tell JavaScript to use strict mode by setting:

"use strict";

at the top of your script. Not declaring your variables as such, will then result in the error you’d expect. Using strict also does some other stuff in the background, but that too is out of the scope of this tutorial.

As a rule of thumb, use strict mode, but always declare your variables.

Different type of functions

In JavaScript there are several types of functions, each with their own mechanics and benefits.

Regular functions (function declaration)

This is the type of function declaration we’ve already been using throughout the tutorial. It is the most common type of function declaration(at least prior to the advent of ES6 arrow functions) and it’s specifics are:

  • Name and function body (functionality) are being hoisted to the top of their respective scope
  • You need to call them to invoke(use) them (myFunction())

Anonymous functions

An anonymous function is a function without a name, and is often used as an argument of another function:

  • Are not being hoisted since they have no name

Function expression

  • Is basically the same as an anonymous function, but stored inside a variable to be used later.
  • Are not being hoisted (unless you use var, in which case only the empty variable name is being hoisted)

IIFE functions

IIFE stands for Immediately Invoked Function Expression and, as the name suggests, are being invoked immediately when you “declare” them. So no reference to it is needed (which is also impossible because they are unnamed)

  • Are being executed immediately upon declaration
  • Can be named or unnamed (anonymous).
  • Can be stored inside a variable, but only to catch the return value, not the function itself.

Arrow functions

Arrow functions are new in ES6 and allow you to omit some of the parts of a regular function declaration, for the purpose of cleaner writing and different scope and this behaviour.

Take the following function expression:

We can rewrite this as an arrow function step by step, making it smaller every step of the way, but keeping the same functionality. Every step along the way is a valid way of writing an es6 arrow function.

  • You can also apply these same steps to unnamed functions and IIFE functions.
  • …restArgs, and …allArgs are also applicable.
  • Supports default parameters (param1 = 20)
  • Supports destructuring within parameters (([a,b] = [30,60]) => a+b)
  • ES6 arrow functions can’t be bound to a this keyword, so it will lexically go up a scope, and use the value of this in the scope in which it was defined.

Constructor functions

In order to understand the next topic, JavaScript's this keyword, better, it’s useful to first dive into the subject of constructor functions.

JavaScript constructor functions are useful when you have a type of object that needs to be created or instantiated multiple times. Such would be the case with for instance: products, people, or anything that has shared, but also unique properties.

Let’s work with the example of products and write a constructor function for them.

Now this may seem like a regular function declaration at first glance, but there a few things happening here. First, we’ve created a function name with a capital. That doesn’t actually do anything, but is done so as a coding convention, to indicate we are dealing with a constructor function.

We then create a const called book and say new Product. What this does is create a new instance of Product, and bind it to the const of book.

What happens in the background, when instantiating a new Product, is this:

Constructor functions are actually more of a topic related to objects. And there is a lot more to a constructor function than we will discuss here. But we will use it to have a look at the behaviour of this.

JavaScript and the this keyword

In the above example, this makes sure that price is bound to the scope/execution context of the function call and essentially the scope of a newly instantiated Product, especially what is inserted into the function (which is the case for price). As a result it remains in the scope where you want it. You will have no strange behaviour when you declare one of the parameters outside of the constructor Function, or when you instantiate other products. It’s a therefor a good means of encapsulation.

Some key points to remember about this :

  • By default, in non "use-strict" mode this refers to the global window object.
  • When using strict mode, this is undefined.
  • If a constructor function is used with the new keyword, this is an object, automatically created in the scope of the newly instantiated constructor function.
  • As a result this is only bound when a function is called (const book = new Product(…)) , not when it is defined, and needs to be bound with .bind(this) when you want to bind this in a function definition.
  • Arrow functions get their value of this from the context in which they are defined, this cannot be bound to an arrow function. Meaning, this refers to the parent (encapsulating) scope.

This may seem hard to comprehend, and it actually is unless you start using it in real life, and see the different behaviour of this in different contexts.

Conclusion

As you’ve probably guessed by now, JavaScript’s custom functions are a vast subject, containing a lot of features and intricacies that weren’t even described in this already quite wordy tutorial. As with any language, expressing yourself in it is highly dependent on the context it’s taking place in. But with the knowledge we’ve summed up here, we got a pretty decent grasp on the more common abilities of custom functions. And a lot of the described features also apply to JavaScript’s own built-in functions.

I’ve certainly learned a lot by writing about it and delving deeper into the subjects. Actively learning about JavaScript in this fashion is a rewarding enterprise and is something I can highly recommend to those studying JavaScript.

Please feel free to point out mistakes I’ve made in the comments, I’m always eager to learn.

A big shout-out to my code-mentor Coen Mooij, for proof-reading and bearing with me during all my impossible questionnaires.

Source material used

For a more thorough (and probably better) explanation of the topics covered in this tutorial, please visit the following pages:

--

--

Marijn Wijbenga
The Startup

Learning front-end development and writing about it to solidify my understanding of the subjects at hand.