📘
NavDoc by Bash School
GithubContact
📘
NavDoc by Bash School
  • 🎓Introduction
  • 🐢Getting Started
  • ⚡Changelog
  • 👨‍🚀Maintainers
  • 🛣️Roadmap
  • Fundamentals
    • The Internet
      • Introduction
      • What is a URL
      • What is a port
      • The DNS protocol
      • The TCP protocol
      • The UDP protocol
      • The Web
      • The HTTP protocol
      • Hyperlinks
      • What is a Web browser
      • What is a Web server
    • HTML
      • Your first HTML page
      • Text tags
      • Attributes
      • Links
      • Images
      • Lists
      • Head Tags
      • Container tags
    • CSS
      • Introduction
      • Colors
      • selectors
      • Cascade
      • Specificity
      • Units
      • Advanced selectors
      • Typography
      • The box model
      • The display property
      • Responsive design
  • JavaScript
    • Basics
      • Introduction
      • Literals , Identifiers, Variables
      • Comments
      • The difference between let, const and var
      • Types
      • Operators and expressions
      • Arithmetic operators
      • The assignment operator
      • Operators precedence
      • Strings
      • Numbers
      • Semicolons, white space and sensitivity
      • Arrays
      • Conditionals
      • Loops
      • Functions
      • Objects
      • Arrays + functions
      • OOPS
      • Asynchronous
      • Scope, hoisting, event loop
      • ES Modules
      • Errors and exceptions
      • Built-in objects
        • The global object
        • Object properties
        • Number
        • String
        • Math
        • JSON
        • Date
        • Intl
        • Set and Map
      • More operators
    • Nodejs
      • Getting Started
      • Installation
      • Hello World in Node
      • Modules
      • Packages
      • File Handling
      • HTTP Request
      • Processing Files
      • HTTP
    • Express.js
      • Getting Started
      • Middleware
      • Serve Static Assets
      • How to Send Files to the Client
      • Sessions
      • Validate Input
      • Sanitizing Data
      • Forms
      • File Uploads
    • React
      • Setting up a React project with Vite
      • React Components
      • Introduction to JSX
      • Using JSX to compose UI
      • The difference between JSX and HTML
      • Embedding JavaScript in JSX
      • Handling user events
      • Managing state
      • Component props
      • Data flow
      • Lifecycle events
      • Managing forms in React
      • Install the React Developer Tools
      • Installing Tailwind CSS in a React app
      • Build a counter in React
    • TypeScript
      • Key Benefits
      • Types of Languages
      • The Need for TypeScript
      • What is TypeScript?
      • The tsc Compiler
      • Basic Types in TypeScript
      • tsconfig
      • Interfaces
      • Types
      • Arrays in TypeScript
      • Enums
      • Exporting and importing
    • MongoDB
      • SQL vs. NoSQL Databases
      • Installing MongoDB
      • MongoDB Databases and Collections
      • Working with Documents
      • MongoDB Operators
      • Sorting, Indexing & Searching
      • Built-in Methods
Powered by GitBook
On this page

Was this helpful?

Edit on GitHub
  1. JavaScript
  2. Basics

Functions

We separate tiny portions of our code into a function so we can call this function multiple times, move it to a separate file, and much more.

In any moderately complex JavaScript program, everything happens inside functions.

Functions are a core, essential part of JavaScript.

What is a function? A function is a block of code, self contained, and we can tell our program to ran that code when we need it.

Here’s a function declaration:

function test() {
  // do something
}

The // do something comment is a placeholder for a set of instructions that can be long as much as you want.

When you want to run this function, you “call it” or “invoke it”, like this:

test()

Function parameters

A function can have no parameter, like we’ve seen previously:

function test() {
  //do something
}

Or it can have one or more parameters, which are declared inside the parentheses:

function test(color) {
  //do something
}

function test(color, age) {
  //do something
}

When we can pass a parameter, we invoke the function passing arguments:

function test(color, age) {
  //do something
}

test("green", 24)
test("black")

The difference between arguments and parameters is this: you define the parameters and that’s what you see inside the function. Arguments are passed by the program when you call the function.

They are basically the value assigned to the parameter.

Functions can have default values for the parameters that are not passed by the caller:

function test(color = 'red', age = 20) {
  console.log(color, age)
}

test("green", 24)
test("black")

Returning values from a function

A function can have a return value.

By default a function returns undefined.

If you want to return a value from it, you add a return keyword with a value:

function test() {
  // do something
  return "hi!"
}

In your program you can assign the return value of the function to a variable, when you invoke the function:

function test() {
  // do something
  return "hi!"
}

const result = test()

result now holds a string with the hi! value.

One thing to note is that you can “return early” from a function, and this is very useful for example when you need to meet a condition before going on with your program:

function test(condition) {
  if (condition === false) return

  // do something
  return "hi!"
}

const result = test(true)

You can only return one value from a function.

A “trick” to return multiple values from a function is to return an object, or an array, like this:

function test() {
  return ["Flavio", 37]
}

Then you can call the function and save your array to a variable, or use array destructuring like this:

const [name, age] = test()

Arrow functions

Arrow functions are very often used instead of “regular” functions, the one I described in the previous chapter. You’ll find both forms used everywhere.

Visually, they allows you to write functions with a shorter syntax, from:

function test() {
  //...
}

to

() => {
  //...
}

But.. notice that we don’t have a name here.

Arrow functions are anonymous. We must assign them to a variable.

We can assign a regular function to a variable, like this:

let test = function test() {
  //...
}

When we do so, we can remove the name from the function:

let test = function() {
  //...
}

and invoke the function using the variable name:

let test = function() {
  //...
}
test()

Although it’s generally recommended to avoid so, for one simple reason: when you have errors, naming your functions helps JavaScript give you back helpful error messages, so you can fix your bugs quicker.

That’s the same thing we do with arrow functions:

let test = () => {
  //...
}
test()

If the function body contains just a single statement, you can omit the parentheses and write all on a single line:

const test = () => console.log('hi!')

Parameters are passed in the parentheses:

const test = (param1, param2) => console.log(param1, param2)

If you have one (and just one) parameter, you could omit the parentheses completely:

const test = param => console.log(param)

Arrow functions allow you to have an implicit return: values are returned without having to use the return keyword.

It works when there is a on-line statement in the function body:

const test = () => 'test'

test() //'test'

Like with regular functions, we can have default parameters, in case they are not passed:

const test = (color = 'black', age = 2) => {
  //do something
}

And as with regular functions, we can only return one value.

The are very similar to regular functions. The big difference with regular functions is when they are used as object methods and their relation to the this keyword, which is something we’ll soon look into.

Nesting functions

Functions can be defined inside other functions:

const test = function() {
  const dosomething = function() {
    /* do something */
  }
  dosomething()

  return "test"
}

Arrow functions can contain other arrow function, or also regular functions. You can mix them:

const test = () => {
  const dosomething = () => {}
  dosomething()
  return "test"
}

The nested function cannot be called from the outside of the enclosing function. Just from inside it.

Immediately-invoked functions

An immediately-invoked function expression (we’ll call them IIFE) is a way to execute functions immediately, as soon as they are created, without waiting for another line of code to call them.

This is the syntax that defines an IIFE:

(function() {
  /* */
})()

We used a pair of parentheses to enclose the function, then we append () to call it.

IIFEs can be defined with arrow functions as well, in the same way:

(() => {
  /* */
})()

We basically have a function defined inside parentheses, and then we append () to execute that function: (THE FUNCTION)().

Those wrapping parentheses are actually what make our function, internally, be considered an expression. Otherwise, the function declaration would be invalid, because we didn’t specify any name.

Remember that function declarations want a name, while function expressions do not require it.

You could also put the invoking parentheses inside the expression parentheses, there is no difference, just a styling preference:

(function() {
  /* */
}())

(() => {
  /* */
}())

IIFEs are one of the rare cases when you need a semicolon in the previous line.

This is because of the strange syntax, JavaScript tries to concatenate the lines, and as it sees a parentheses opening, it thinks the previous line was a function name and now we add a parameter.

To fix this problem you might see this:

;(function() {
  /* */
})()

This completely prevents the issue, so it’s a good practice when you write an IIFE.

Why do we need them sometimes?

We have 2 cases.

One is isolation from the outside.

Functions create a new scope, so any variable defined inside an IIFE is not going to “leak” outside. This is something very common when you have multiple pieces of JavaScript running in a page, that should not interact with each other. The best example of this is a WordPress site that has a dozen plugins all adding their own JavaScript.

If by chance two define a variable or a function with the same name, we have compatibility issues. IIFEs should help you isolate this kind of problem.

You can use this technique in your own code to isolate variables within a IIFE.

Another use case is when you have code that runs inside a loop, and you must do something asynchronously. We haven’t talked about asynchronous code yet, so we’ll leave this explanation for another lesson.

Recursive functions

A function has the ability to call itself.

This is what we call recursion.

Recursion allows us to solve some specific problems in a smart way.

You need a function with a name, which can be either a “regular” function, or an arrow function:

function test() {

}

or 

const test = () => {

}

Now you can call test() inside test().

function test() {
  test()
}

//or

const test = () => {
	test()
}

The simplest example we can make is calculating a factorial of a number.

The factorial of a number is what we get by multiplying the number for (number - 1), (number - 2), and so on until we reach the number 1.

The factorial of 3 is

(3 * (3 - 1) * (3 - 2)) = 3 * 2 * 1

which is 6

The factorial of 4 is

(4 * (4 - 1) * (4 - 2) * (4 - 3)) = 4 * 3 * 2 * 1

which is 24.

We can create a recursive function to calculate the factorial automatically:

function factorial(n) {
  return n >= 1 ? n * factorial(n - 1) : 1
}

Now we can call this function passing the number for which we want to calculate the factorial.

factorial(1) //1
factorial(2) //2
factorial(3) //6
factorial(4) //24

We can also use an arrow function if we prefer, there’s no difference:

const factorial = (n) => {
  return n >= 1 ? n * factorial(n - 1) : 1
}

With recursive functions you have to pay attention to not generating an error by calling a function an infinite amount of times.

What do I mean? Imagine we do an error, and instead of calculating the factorial as

const factorial = (n) => {
  return n >= 1 ? n * factorial(n - 1) : 1
}

we do this:

const factorial = (n) => {
  return n >= 1 ? n * factorial(n) : 1
}

As you can see, instead of decreasing n each time we call factorial(), we are now calling factorial(n) ad infinitum. There’s no end, because we forgot to lower it on every call.

If you run this code, you’ll get this error:

RangeError: Maximum call stack size exceeded

Every time a function is invoked, JavaScript needs to remember the current context before switching to the new one, so it puts that context on the call stack. As soon as the function returns, JavaScript goes to the call stack and picks the last element that was added, and resumes its execution.

Maximum call stack size exceeded means that too many elements were put on the stack, and your program crashed.

It’s similar to having an infinite loop, because the program can’t continue its execution.

PreviousLoopsNextObjects

Last updated 1 year ago

Was this helpful?