Functions

Functions package code into reusable chunks

A function we’ve used

drawLine(10, 20, 30, 40, 'blue');

Draws a blue line from x,y coordinates 10,20 to 30,40

We don’t have to know how it works, just what it does.

We “call” functions

Using it like this is described as “calling” it.

As in, “Call the drawLine function to draw a line.”

Functions take arguments

The stuff in ()s after the name of the function in a function call is a comma-delimited list of expressions.

The expressions are each evaluated to get a list of values that are “passed” to the function.

The values are called the “arguments” to the function.

As in “We passed the arguments 10, 20, 30, 40, and 'blue' to the drawLine function.”

Some functions take zero arguments

Math.random() ⟹ 0.7388842248424481

We still need the ()s to indicate that we’re calling the function.

An example of why we might want to write a function

Here’s some code to draw a triangle

drawLine(10, 20, 30, 40, 'blue');
drawLine(30, 40, 50, 60, 'blue');
drawLine(50, 60, 10, 20, 'blue');

Note how these three calls to drawLine add up to a triangle by connecting the three points 10,20, 30,40, and 50,60.

This code draws a different triangle

drawLine(10, 20, 30, 40, 'blue');
drawLine(30, 40, 10, 40, 'blue');
drawLine(10, 40, 10, 20, 'blue');

Same pattern but maybe a bit harder to see because of the common x and y values in some of the points.

We could use variables to capture this common pattern

First define six variables

let x1 = 10;
let y1 = 20;
let x2 = 30;
let y2 = 40;
let x3 = 50;
let y3 = 60;

The names could be anything.

But we should choose names that suggest their purpose.

Then use the variables instead of literal values

Using variables as the arguments to drawLine make the pattern a bit easier to see.

drawLine(x1, y1, x2, y2, 'blue');
drawLine(x2, y2, x3, y3, 'blue');
drawLine(x3, y3, x1, y1, 'blue');

With the variable definitions just given, this draws the first triangle.

Code to draw the second triangle

Reset the variables

// Variables already exist, just give them new values
x1 = 10;
y1 = 20;
x2 = 30;
y2 = 40;
x3 = 10;
y3 = 40;

Then use them

drawLine(x1, y1, x2, y2, 'blue');
drawLine(x2, y2, x3, y3, 'blue');
drawLine(x3, y3, x1, y1, 'blue');

Note that these three lines are exactly the same as before.

This approach has some advantages

We can mentally “chunk” the three lines of code that draw the triangle so whenever we see them, we know what’s happening.

And the use of variables in the calls to drawLine make the pattern a bit more clear.

But there are also problems

The obvious one is the massive duplication of code.

Even worse is the lack of abstraction: we know the three calls to drawLine mean “draw a triangle” but we have to look at the details of the code to really see it.

Also there are issues with managing the variables

Need to make sure no other code is using those variable names.

Functions solve these problems

Defining a function

const drawTriangle = (x1, y1, x2, y2, x3, y3) => {
  drawLine(x1, y1, x2, y2, 'blue');
  drawLine(x2, y2, x3, y3, 'blue');
  drawLine(x3, y3, x1, y1, 'blue');
};

This is defining a variable named drawTriangle whose value is a function.

There’s a lot going on here so let’s take it apart.

The variable part

The first thing is just a normal variable definition …

const drawTriangle = ...

… defining a variable named drawTriangle whose value, once set, will never change.

The function value

The value we assign to that variable is this:

(x1, y1, x2, y2, x3, y3) => {
  drawLine(x1, y1, x2, y2, 'blue');
  drawLine(x2, y2, x3, y3, 'blue');
  drawLine(x3, y3, x1, y1, 'blue');
}

We can break this down into two parts.

The argument list

A comma-separated list of names. In this case:

(x1, y1, x2, y2, x3, y3)

These are variables which will hold the values passed as arguments when the function is called.

Thus argument lists are a third way of defining variables.

The body

Code enclosed in {}s. In this case:

{
  drawLine(x1, y1, x2, y2, 'blue');
  drawLine(x2, y2, x3, y3, 'blue');
  drawLine(x3, y3, x1, y1, 'blue');
}

This code will run when the function is called.

When it runs, the variables defined in the argument list will hold the values passed as arguments.

Basic structure of a function

(arguments) => { code }

Calling the function

drawTriangle(10, 20, 30, 40, 50, 60);
drawTriangle(10, 20, 30, 40, 10, 40);

Each of these calls replaces six lines of setting up variables and three lines of calling drawLine and makes the meaning of the code much more clear.

Function calls are also expressions

Calling a function returns a value.

We’ve seen this already:

Math.sqrt(16) ⟹ 4

However not all functions return useful values

In the simple-draw REPL you can evaluate this:

drawLine(0, 0, width, height, 'black')

It will draw a line but the value it returns in the REPL is undefined.

There are two flavors of functions

Functions that do something, e.g. draw something on the screen or print something out.

Functions that compute a value and return it.

It is generally a good idea to write functions that are one or the other, not both.

drawLine is the first kind

We call it not to get a value but because it does something we want.

Sometimes we say we call these functions for their side effects.

They usually return undefined, i.e. no particular value.

Math.sqrt is the second kind

It has no side effects but it does return a useful value.

Writing a new function that computes a value

const double = (n) => {
  return n * 2;
};

The name is double. It takes one argument, n, and returns the value n times 2.

double(16) ⟹ 32

return

return is special construct in Javascript that causes a function to immediately return the value of the following expression.

A shorthand form

If the body of the function, as in double, consists of just a single return statement, you can write the function without the return and without the {}s like this:

const double = (n) => n * 2;

This is equivalent to the earlier version with the explicit return.

As a kind of expression, function calls can be used anywhere a value is needed

» double(16) * 3
96

double(16) evaluates to 32 which is then multipled by 3, giving us a final value of 96.

» double(double(16))
64

In this case the double(16) is evaluted, as before, to 32 which then becomes the argument to the outer call to double which doubles it and returns 64.

What you can do in a function body

Pretty much anything

Create variables to hold useful intermediate values

const manhattanDistance = (x1, y1, x2, y2) => {
  const xDistance = Math.abs(x1 - x2);
  const yDistance = Math.abs(y1 - y2);
  return xDistance + yDistance;
};

These variables only exist within the function body so you don’t have to worry about other variables with the same name elsewhere in your program.

Call other functions

const distance = (a, b) => Math.abs(a - b);

const manhattanDistance = (x1, y1, x2, y2) => {
  return distance(x1, x2) + distance(y1, y2);
};

There's more but that's plenty for now

Up next

Control constructs