Linux Professional Institute Learning Logo.
Skip to main content
  • Home
    • All Resources
    • LPI Learning Materials
    • Become a Contributor
    • Publishing Partners
    • Become a Publishing Partner
    • About
    • FAQ
    • Contributors
    • Roadmap
    • Contact
  • LPI.org
034.3 Lesson 2
Topic 031: Software Development and Web Technologies
031.1 Software Development Basic
  • 031.1 Lesson 1
031.2 Web Application Architecture
  • 031.2 Lesson 1
031.3 HTTP Basics
  • 031.3 Lesson 1
Topic 032: HTML Document Markup
032.1 HTML Document Anatomy
  • 032.1 Lesson 1
032.2 HTML Semantics and Document Hierarchy
  • 032.2 Lesson 1
032.3 HTML References and Embedded Resources
  • 032.3 Lesson 1
032.4 HTML Forms
  • 032.4 Lesson 1
Topic 033: CSS Content Styling
033.1 CSS Basics
  • 033.1 Lesson 1
033.2 CSS Selectors and Style Application
  • 033.2 Lesson 1
033.3 CSS Styling
  • 033.3 Lesson 1
033.4 CSS Box Model and Layout
  • 033.4 Lesson 1
Topic 034: JavaScript Programming
034.1 JavaScript Execution and Syntax
  • 034.1 Lesson 1
034.2 JavaScript Data Structures
  • 034.2 Lesson 1
034.3 JavaScript Control Structures and Functions
  • 034.3 Lesson 1
  • 034.3 Lesson 2
034.4 JavaScript Manipulation of Website Content and Styling
  • 034.4 Lesson 1
Topic 035: NodeJS Server Programming
035.1 NodeJS Basics
  • 035.1 Lesson 1
035.2 NodeJS Express Basics
  • 035.2 Lesson 1
  • 035.2 Lesson 2
035.3 SQL Basics
  • 035.3 Lesson 1
How to get certified
  1. Topic 034: JavaScript Programming
  2. 034.3 JavaScript Control Structures and Functions
  3. 034.3 Lesson 2

034.3 Lesson 2

Certificate:

Web Development Essentials

Version:

1.0

Topic:

034 JavaScript Programming

Objective:

034.3 JavaScript Control Structures and Functions

Lesson:

2 of 2

Introduction

In addition to the standard set of builtin functions provided by the JavaScript language, developers can write their own custom functions to map an input to an output suitable to the application’s needs. Custom functions are basically a set of statements encapsulated to be used elsewhere as part of an expression.

Using functions is a good way to avoid writing duplicate code, because they can be called from different locations throughout the program. Moreover, grouping statements in functions facilitates the binding of custom actions to events, which is a central aspect of JavaScript programming.

Defining a Function

As a program grows, it becomes harder to organize what it does without using functions. Each function has its own private variable scope, so the variables defined inside a function will be available only inside that same function. Thus, they won’t get mixed up with variables from other functions. Global variables are still accessible from within functions, but the preferable way to send input values to a function is through function parameters. As an example, we are going to build on the prime number validator from the previous lesson:

// A naive prime number tester

// The number we want to evaluate
let candidate = 231;

// Auxiliary variable
let is_prime = true;

// Start with the lowest prime number after 1
let factor = 2;

// Keeps evaluating while factor is less than the candidate
while ( factor < candidate )
{

  if ( candidate % factor == 0 )
  {
    // The remainder is zero, so the candidate is not prime
    is_prime = false;
    break;
  }

  // The next number that will divide the candidate
  factor++;
}

// Display the result in the console window
if ( is_prime )
{
  console.log(candidate, "is prime");
}
else
{
  console.log(candidate, "is not prime");
}

If later in the code you need to check if a number is prime, it would be necessary to repeat the code that has already been written. This practice is not recommended, because any corrections or improvements to the original code would need to be replicated manually everywhere the code was copied to. Moreover, repeating code places a burden on the browser and network, possibly slowing down the display of the web page. Instead of doing this, move the appropriate statements to a function:

// A naive prime number tester function
function test_prime(candidate)
{
  // Auxiliary variable
  let is_prime = true;

  // Start with the lowest prime number after 1
  let factor = 2;

  // Keeps evaluating while factor is less than the candidate
  while ( factor < candidate )
  {

    if ( candidate % factor == 0 )
    {
      // The remainder is zero, so the candidate is not prime
      is_prime = false;
      break;
    }

    // The next number that will divide the candidate
    factor++;
  }

  // Send the answer back
  return is_prime;
}

The function declaration starts with a function statement, followed by the name of the function and its parameters. The name of the function must follow the same rules as the names for variables. The parameters of the function, also known as the function arguments, are separated by commas and enclosed by parenthesis.

Tip

Listing the arguments in the function declaration is not mandatory. The arguments passed to a function can be retrieved from an array-like arguments object inside that function. The index of the arguments starts at 0, so the first argument is arguments[0], the second argument is arguments[1], and so on.

In the example, the test_prime function has only one argument: the candidate argument, which is the prime number candidate to be tested. Function arguments work like variables, but their values are assigned by the statement calling the function. For example, the statement test_prime(231) will call the test_prime function and assign the value 231 to the candidate argument, which will then be available inside the function’s body like an ordinary variable.

If the calling statement uses simple variables for the function’s parameters, their values will be copied to the function arguments. This procedure — copying the values of the parameters used in the calling statement to the parameters used inside the function —- is called passing arguments by value. Any modifications made to the argument by the function does not affect the original variable used in the calling statement. However, if the calling statement uses complex objects as arguments (that is, an object with properties and methods attached to it) for the function’s parameters, they will be passed as reference and the function will be able to modify the original object used in the calling statement.

The arguments that are passed by value, as well as the variables declared within the function, are not visible outside it. That is, their scope is restricted to the body of the function where they were declared. Nonetheless, functions are usually employed to create some output visible outside the function. To share a value with its calling function, a function defines a return statement.

For instance, the test_prime function in the previous example returns the value of the is_prime variable. Therefore, the function can replace the variable anywhere it would be used in the original example:

// The number we want to evaluate
let candidate = 231;

// Display the result in the console window
if ( test_prime(candidate) )
{
  console.log(candidate, "is prime");
}
else
{
  console.log(candidate, "is not prime");
}

The return statement, as its name indicates, returns control to the calling function. Therefore, wherever the return statement is placed in the function, nothing following it is executed. A function can contain multiple return statements. This practice can be useful if some are within conditional blocks of statements, so that the function might or might not execute a particular return statement on each run.

Some functions may not return a value, so the return statement is not mandatory. The function’s internal statements are executed regardless of its presence, so functions can also be used to change the values of global variables or the contents of objects passed by reference, for example. Notwithstanding, if the function does not have a return statement, its default return value is set to undefined: a reserved variable that does not have a value and cannot be written.

Function Expressions

In JavaScript, functions are just another type of object. Thus, functions can be employed in the script like variables. This characteristic becomes explicit when the function is declared using an alternative syntax, called function expressions:

let test_prime = function(candidate)
{
  // Auxiliary variable
  let is_prime = true;

  // Start with the lowest prime number after 1
  let factor = 2;

  // Keeps evaluating while factor is less than the candidate
  while ( factor < candidate )
  {

    if ( candidate % factor == 0 )
    {
      // The remainder is zero, so the candidate is not prime
      is_prime = false;
      break;
    }

    // The next number that will divide the candidate
    factor++;
  }

  // Send the answer back
  return is_prime;
}

The only difference between this example and the function declaration in the previous example is in the first line: let test_prime = function(candidate) instead of function test_prime(candidate). In a function expression, the test_prime name is used for the object containing the function and not to name the function itself. Functions defined in function expressions are called in the same way as functions defined using the declaration syntax. However, whereas declared functions can be called before or after their declaration, function expressions can be called only after their initialization. Like with variables, calling a function defined in an expression before its initialization will cause a reference error.

Function Recursion

In addition to executing statements and calling builtin functions, custom functions can also call other custom functions, including themselves. To call a function from itself is called function recursion. Depending on the type of problem you are trying to solve, using recursive functions can be more straightforward than using nested loops to perform repetitive tasks.

So far, we know how to use a function to test whether a given number is prime. Now suppose you want to find the next prime following a given number. You could employ a while loop to increment the candidate number and write a nested loop which will look for integer factors for that candidate:

// This function returns the next prime number
// after the number given as its only argument
function next_prime(from)
{
  // We are only interested in the positive primes,
  // so we will consider the number 2 as the next
  // prime after any number less than two.
  if ( from < 2 )
  {
    return 2;
  }

  // The number 2 is the only even positive prime,
  // so it will be easier to treat it separately.
  if ( from == 2 )
  {
    return 3;
  }

  // Decrement "from" if it is an even number
  if ( from % 2 == 0 )
  {
    from--;
  }

  // Start searching for primes greater then 3.

  // The prime candidate is the next odd number
  let candidate = from + 2;

  // "true" keeps the loop going until a prime is found
  while ( true )
  {
    // Auxiliary control variable
    let is_prime = true;

    // "candidate" is an odd number, so the loop will
    // try only the odd factors, starting with 3
    for ( let factor = 3; factor < candidate; factor = factor + 2 )
    {
      if ( candidate % factor == 0 )
      {
        // The remainder is zero, so the candidate is not prime.
        // Test the next candidate
        is_prime = false;
        break;
      }
    }
    // End loop and return candidate if it is prime
    if ( is_prime )
    {
      return candidate;
    }
    // If prime not found yet, try the next odd number
    candidate = candidate + 2;
  }
}

let from = 1024;
console.log("The next prime after", from, "is", next_prime(from));

Note that we need to use a constant condition for the while loop (the true expression inside the parenthesis) and the auxiliary variable is_prime to know when to stop the loop. Athough this solution is correct, using nested loops is not as elegant as using recursion to perform the same task:

// This function returns the next prime number
// after the number given as its only argument
function next_prime(from)
{
  // We are only interested in the positive primes,
  // so we will consider the number 2 as the next
  // prime after any number less than two.
  if ( from < 2 )
  {
    return 2;
  }

  // The number 2 is the only even positive prime,
  // so it will be easier to treat it separately.
  if ( from == 2 )
  {
    return 3;
  }

  // Decrement "from" if it is an even number
  if ( from % 2 == 0 )
  {
    from--;
  }

  // Start searching for primes greater then 3.

  // The prime candidate is the next odd number
  let candidate = from + 2;

  // "candidate" is an odd number, so the loop will
  // try only the odd factors, starting with 3
  for ( let factor = 3; factor < candidate; factor = factor + 2 )
  {
    if ( candidate % factor == 0 )
    {
      // The remainder is zero, so the candidate is not prime.
      // Call the next_prime function recursively, this time
      // using the failed candidate as the argument.
      return next_prime(candidate);
    }
  }

  // "candidate" is not divisible by any integer factor other
  // than 1 and itself, therefore it is a prime number.
  return candidate;
}

let from = 1024;
console.log("The next prime after", from, "is", next_prime(from));

Both versions of next_prime return the next prime number after the number given as its only argument (from). The recursive version, like the previous version, starts by checking the special cases (i.e. numbers less or equal to two). Then it increments the candidate and starts looking for any integer factors with the for loop (notice that the while loop is not there anymore). At that point, the only even prime number has already been tested, so the candidate and its possible factors are incremented by two. (An odd number plus two is the next odd number.)

There are only two ways out of the for loop in the example. If all the possible factors are tested and none of them has a remainder equal to zero when dividing the candidate, the for loop completes and the function returns the candidate as the next prime number after from. Otherwise, if factor is an integer factor of candidate (candidate % factor == 0), the returned value comes from the next_prime function called recursively, this time with the incremented candidate as its from parameter. The calls for next_prime will be stacked on top of one another, until one candidate finally turns up no integer factors. Then the last next_prime instance containing the prime number will return it to the previous next_prime instance, and thus successively down to the first next_prime instance. Even though each invocation of the function uses the same names for variables, the invocations are isolated from each other, so their variables are kept separated in memory.

Guided Exercises

  1. What kind of overhead can developers mitigate by using functions?

  2. What is the difference between function arguments passed by value and function arguments passed by reference?

  3. Which value will be used as the output of a custom function if it does not have a return statement?

Explorational Exercises

  1. What is the probable cause of an Uncaught Reference Error issued when calling a function declared with the expression syntax?

  2. Write a function called multiples_of that receives three arguments: factor, from, and to. Inside the function, use the console.log() instruction to print all multiples of factor lying between from and to.

Summary

This lesson covers how to write custom functions in JavaScript code. Custom functions let the developer divide the application in “chunks” of reusable code, making it easier to write and maintain larger programs. The lesson goes through the following concepts and procedures:

  • How to define a custom function: function declarations and function expressions.

  • Using parameters as the function input.

  • Using the return statement to set the function output.

  • Function recursion.

Answers to Guided Exercises

  1. What kind of overhead can developers mitigate by using functions?

    Functions allow us to reuse code, which facilitates code maintenance. A smaller script file also saves memory and download time.

  2. What is the difference between function arguments passed by value and function arguments passed by reference?

    When passed by value, the argument is copied to the function and the function is not able to modify the original variable in the calling statement. When passed by reference, the function is able to manipulate the original variable used in the calling statement.

  3. Which value will be used as the output of a custom function if it does not have a return statement?

    The returned value will be set to undefined.

Answers to Explorational Exercises

  1. What is the probable cause of an Uncaught Reference Error issued when calling a function declared with the expression syntax?

    The function was called before its declaration in the script file.

  2. Write a function called multiples_of that receives three arguments: factor, from, and to. Inside the function, use the console.log() instruction to print all multiples of factor lying between from and to.

      function multiples_of(factor, from, to)
      {
        for ( let number = from; number <= to; number++ )
        {
          if ( number % factor == 0 )
          {
            console.log(factor, "*", number / factor, "=", number);
          }
        }
      }

Linux Professional Insitute Inc. All rights reserved. Visit the Learning Materials website: https://learning.lpi.org
This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

Next Lesson

034.4 JavaScript Manipulation of Website Content and Styling (034.4 Lesson 1)

Read next lesson

Linux Professional Insitute Inc. All rights reserved. Visit the Learning Materials website: https://learning.lpi.org
This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

LPI is a non-profit organization.

© 2023 Linux Professional Institute (LPI) is the global certification standard and career support organization for open source professionals. With more than 200,000 certification holders, it's the world’s first and largest vendor-neutral Linux and open source certification body. LPI has certified professionals in over 180 countries, delivers exams in multiple languages, and has hundreds of training partners.

Our purpose is to enable economic and creative opportunities for everybody by making open source knowledge and skills certification universally accessible.

  • LinkedIn
  • flogo-RGB-HEX-Blk-58 Facebook
  • Twitter
  • Contact Us
  • Privacy and Cookie Policy

Spot a mistake or want to help improve this page? Please let us know.

© 1999–2023 The Linux Professional Institute Inc. All rights reserved.