1. JavaScript
  2. Fundamentals
  3. Functions

JavaScript Functions

Last updated:

A function is a group of instructions that together perform a task; it is defined once and can be used (i.e. called) as many times as needed.

Declaring a function

function sayHello(firstname) {
  return "Hello " + firstname;
}

Using a function:

sayHello("Paul");  // returns: "Hello Paul"

Returned value

Always returns a value; if nothing is specified, undefined is returned:

function sayHello(firstname) {
  console.log("Hello " + firstname);
}
console.log(sayHello("Paul"));  // Outputs: undefined

Default arguments

function sayHello1(firstname) {
  return "Hello " + firstname;
}
console.log(sayHello1());  // Outputs: "Hello undefined"

function sayHello2(firstname = "World") {
  return "Hello " + firstname;
}
console.log(sayHello2());  // Outputs: "Hello World"

arguments object

A special arguments object holds a list of all arguments passed to a JavaScript function even if none are specified in its definition.</p>

function sayHello(firstname, site) {
  console.log(`Hello ${firstname} from ${site}!`);
}
sayHello("Paul", "pardel.dev"); // returns: "Hello Paul from pardel.dev!"
function sayHello() {
  console.log(arguments);
}
sayHello("Paul", "pardel.dev"); // returns: [Arguments] { '0': 'Paul', '1': 'pardel.dev' }
console.log(typeof arguments); // Outputs: object

We can check how many were passed and even list all arguments:

function sayHello() {
  console.log(arguments.length);
  for(let arg of arguments) {
    console.log(arg);
  }
}
sayHello("Paul", "pardel.dev");
// Outputs:
// 2
// Paul
// pardel.dev

Using arguments

function sayHello() {
  console.log(`Hello ${arguments[0]} from ${arguments[1]}!`);
}
sayHello("Paul", "pardel.dev"); // returns: "Hello Paul from pardel.dev!"

Can be modified on the fly:

function sayHello() {
  arguments[0] = "World";
  console.log(`Hello ${arguments[0]} from ${arguments[1]}!`);
}
sayHello("Paul", "pardel.dev"); // Outputs: "Hello World from pardel.dev!"

Anonymous functions

A function without a name:

let sayHello = function(firstname) {
  return "Hello " + firstname;
}

console.log(sayHello("Paul"));  // Outputs: "Hello Paul"

Functions as parameters

setTimeout(function sayHello() {
  console.log("Hello World");
}, 1000);

sayHello();  // returns: "ReferenceError: sayHello is not defined"

setTimeout(function() {
  console.log("Hello World");
}, 1000);

Nameless functions that are used extensively in methods that take function parameters:

let myArray = [1, 4, 42, 11, 79];
let doubleArray = myArray.map(function (item) {
  return item * 2;
});
console.log(doubleArray); // returns: [2, 8, 84, 22, 158]

Arrow functions

Provide a more concise way to write functions:

let myArray = [1, 4, 42, 11, 79];
let doubleArray = myArray.map( item => {
  return item * 2;
});
console.log(doubleArray); // returns: [2, 8, 84, 22, 158]

Limitations:

  • no own bindings - this or super is not available
  • yield cannot be used insite them

Usage: DOM elements management

e.g: triggering actions when a button is clicked is now as simple as:

let signupButton = document.getElementById("signup");
signupButton.click(event => {
  console.log(`New signup: ${signupButton}`);
});

Variadic Functions

Accept an arbitrary number of arguments when called - provides flexibility and convenience, particularly when working with functions that require varying numbers of inputs.

rest operator

The rest syntax is denoted by three dots (…) before the function parameter name:

function sum(...numbers) {
  return numbers.reduce((total, current) => total + current, 0);
}
function multiply(...numbers) {
  return numbers.reduce((total, current) => total * current, 1);
}

console.log(sum(1, 2, 3, 4, 5, 6));       // returns: 21
console.log(multiply(1, 2, 3, 4, 5, 6));  // returns: 720

Usage: Variable Number of Parameters

function greet(greeting, ...names) {
  names.forEach(name => console.log(`${greeting}, ${name}!`));
}

greet('Good morning', 'Alice', 'Bob', 'Charlie');
// Outputs:
//   Good morning, Alice!
//   Good morning, Bob!
//   Good morning, Charlie!

Usage: Merging arrays

function mergeArrays(arr1, arr2, ...rest) {
  return [...arr1, ...arr2, ...rest];
}

const array1 = [1, 2, 3];
const array2 = [4, 5, 6];
const array3 = [7, 8, 9];

const mergedArray = mergeArrays(array1, array2, ...array3);
console.log(mergedArray);  // Outputs: [1, 2, 3, 4, 5, 6, 7, 8, 9]

A slightly simpler version of mergeArrays:

function mergeArrays(...arrays) {
  return [].concat(...arrays);
}

const array1 = [1, 2, 3];
const array2 = [4, 5, 6];
const array3 = [7, 8, 9];

const mergedArray = mergeArrays(array1, array2, array3);
console.log(mergedArray);  // Outputs: [1, 2, 3, 4, 5, 6, 7, 8, 9]

Usage: Sorting by multiple properties

function sortByProperties(property, ...rest) {
  return function (a, b) {
    if (a[property] < b[property]) {
      return -1;
    } else if (a[property] > b[property]) {
      return 1;
    } else {
      if (rest.length === 0) {
        return 0;
      } else {
        const [nextProperty, ...remainingProperties] = rest;
        return sortByProperties(nextProperty, ...remainingProperties)(a, b);
      }
    }
  };
}

const people = [
  { name: 'Alice', age: 30, city: 'New York' },
  { name: 'Bob', age: 30, city: 'San Francisco' },
  { name: 'Charlie', age: 25, city: 'London' },
];

const sortedPeople = people.sort(sortByProperties('age', 'city', 'name'));
console.log(sortedPeople);
// Outputs:
// [
//   { name: 'Charlie', age: 25, city: 'London' },
//   { name: 'Alice', age: 30, city: 'New York' },
//   { name: 'Bob', age: 30, city: 'San Francisco' }
// ]

Usage: Dynamic object property assignment

function createPerson(name, age, ...extra) {
  const person = {
    name,
    age,
  };

  for (const prop in extra) {
    person[prop] = extra[prop];
  }

  return person;
}

const person = createPerson("Charlie", 30,
    { city: "London", country: "UK" });
console.log(person);
// Outputs: { '0': { city: 'London', country: 'UK' }, name: 'Charlie', age: 30 }

IIFE (Immediately Invoked Function Expression)

A design pattern that allows one to define and execute a function immediately upon its declaration. It’s a way of creating and running a function without explicitly naming or calling it:

(function() {
    console.log('Hello World!');
})();

NB: Notice the final pair of parentheses: () - these parentheses are what actually call and execute the function.

Usage:Counter Module

A simple counter module that exposes methods to increment and display the current count, without exposing the internal count variable:

const counter = (function() {
    let count = 0;
    let step = 1;

    function increment(newStep) {
        if(newStep) { step = newStep; }
        count += step;
    }

    function display() {
        console.log(`Current count: ${count}`);
    }

    return {
        increment,
        display
    };
})();

counter.increment();
counter.display();   // Outputs: "Current count: 1"
counter.increment(3);
counter.display();   // Outputs: "Current count: 4"
counter.increment();
counter.display();   // Outputs: "Current count: 7"

Usage: One-time Event Listener

Add an event listener to a button that should only execute once and then remove itself:

<button id="myBtn">One time action</button>
(function() {
    const btn = document.getElementById('myBtn');
    if(!btn) { return; }

    function handleClick() {
        console.log('One time click done!');
        btn.removeEventListener('click', handleClick);
    }

    btn.addEventListener('click', handleClick);
})();

Usage: Configuration Setup

const config = {};

(function() {
    function setDefaults() {
        config.apiKey = 'YOUR_API_KEY';
        config.apiUrl = 'https://api.example.com';
        config.timeout = 5000;
    }

    // Initial config processing
    function processSettings() {
        console.log(`Configuring with API key: ${config.apiKey}`);
    }

    setDefaults();
    processSettings();
})();

console.log(config);
// prints: { apiKey: 'YOUR_API_KEY',
//           apiUrl: 'https://api.example.com',
//           timeout: 5000 }

Why Use IIFE?

  • encapsulation and scoping: IIFE creates a new scope, which means any variables declared within the function will not be accessible outside of it. This can help prevent variable name collisions and keeps the global scope clean.
  • privacy: Since variables and functions declared inside an IIFE are not accessible from outside, they remain private. This is useful when you want to create a module or a piece of code with a well-defined interface while hiding its internal implementation details.
  • self-contained code: IIFE can be useful when you want to create a self-contained piece of code that doesn’t rely on external variables or functions. This can be helpful for code organization and maintainability.

Built-in Functions

Extensive collection of built-in functions:

Generic

  • isNaN - check is an expression is Not a Number:

      isNaN("Hello");   // returns: true
      isNaN(42);        // returns: false
      isNaN(NaN);       // returns: true
      isNaN(undefined); // returns: true
      isNaN(null);      // returns: false
    
  • eval - evaluates a string as JavaScript code and executes it; should be used with caution, due to security risks if used with untrusted input:

      eval("2 + 5");                         // returns: 7
      eval("console.log('Hello World!');");  // Outputs: Hello World!
    
  • parseInt - converts a string into an integer; takes an optional base to be used for conversion:

      parseInt("42");         // returns: 42
      parseInt("42 apples");  // returns: 42
      parseInt("apples 42");  // returns: NaN
      parseInt("101010",2);   // returns: 42
      parseInt("101010",16);  // returns: 1052688
    
  • parseFloat - converts a string into a floating-point number:

      parseFloat("42");         // returns: 42
      parseFloat("42.1");       // returns: 42.1
      parseFloat("42.1 C");     // returns: 42.1
      parseFloat("C 42.1");     // returns: NaN
    

Object Prototype

  • assign - copy values from one or more source objects to a target object
  • create - creates a new object with the specified prototype object and properties
  • keys - returns an array of a given object’s property names
  • values - returns an array of a given object’s property values
  • entries - returns an array of a given object’s own enumerable property [key, value] pairs

      const target = { a: 1 };
      const source1 = { b: 2 };
      const source2 = { c: 3 };
    
      const result = Object.assign(target, source1, source2);
      console.log(result);  // Outputs: { a: 1, b: 2, c: 3 }
    
      Object.keys(result);    // returns: ['a', 'b', 'c']
      Object.values(result);  // returns: [1, 2, 3]
      Object.entries(result); // returns: [['a', 1], ['b', 2], ['c', 3]]
    
      Object.freeze(result);
      result.a = 2; // This assignment will be silently ignored
      console.log(result.a); // Outputs: 1
    
  • freeze - freezes an object, preventing any changes to its properties

      const result = { a: 1, b: 2, c: 3 };
    
      Object.freeze(result);
      result.a = 2; // This assignment will be silently ignored
      console.log(result.a); // 1
    
  • seal - seals an object, preventing any new properties from being added to it and existing properties from being removed:

      const result = { a: 1, b: 2, c: 3 };
      Object.seal(result);
      result.a = 2; // This assignment is allowed
      delete result.b; // returns: false
      result.d = 5; // silently ignored
      console.log(result); // Outputs: { a: 2, b: 2, c: 3 }
    

Math

  • Math.round() - rounds to nearest integer.
  • Math.floor() - rounds downward to nearest integer.
  • Math.ceil() - rounds a number upward to the nearest integer.
  • Math.random() - returns a random number between 0 and 1.
  • Math.min() and Math.max() - return the minimum and maximum values among the arguments, respectively.

Date

  • Date.now() - returns number of milliseconds elapsed since January 1, 1970, 00:00:00 UTC.
  • getDate() - returns the day of the month for the specified date.
  • getMonth() - returns the month for the specified date.
  • getFullYear() - returns the year for the specified date.
  • setTime() - sets the date and time by adding or subtracting a specified number of milliseconds to/from midnight January 1, 1970.

JSON

  • JSON.parse() - parses a JSON string and converts it into a JavaScript object.
  • JSON.stringify() - converts a JavaScript object or value to a JSON string.