We can define our own functions that we can call elsewhere in our own program:

function hello (person) {
  console.log("Hello, " + person + "!");
}

hello("Dolly");
hello("World");

Here the function hello is being defined. The keyword function here is kind of like var in that it's creating a variable where the function will live. The function's name is hello, so the function will live in the hello variable, so we can use it later.

Formatting

The hello function is followed by a space and parentheses. The parentheses form the parameter declaration: what variables will be used to capture values passed to the function.

The { } curly braces here create a group of statements, called a block. The statements contained within will become the body of the function.

Coding Style and Indentation

function terriblyFormatted(poorleeSpelld)
{
var inconsistent      = "spacing" ;
    if  ( condition ==="notOnOwnLine") { callNotObvious    (
      bracesNotEasyToSeePair();
      missingSemicolon()
     );
}
  }

function wellFormatted(correctSpelling) {
  var oneSpace = "around operators";
  if (condition === "onOwnLine") {
    doAThing();
    bracesEasyToSeePair();
    semicolonsPresent();
  }
}

Coding style varies from language to language. JavaScript doesn't really mind where we put some symbols -- white space and line breaks are pretty much ignored by JavaScript. We humans however find them very useful.

Both the examples above are valid JavaScript, but wellFormatted is much easier to read, modify and thus maintain.

There are many popular 'coding guides', but we will teach you the most common, often called 1TCS or One True Coding Style.

Different teams and projects use different styles for different languages. HTML will have one, CSS another, JavaScript yet another and so on.

Curly braces are usually placed one space after the parameter parentheses to open, and on a line by itself to close. The closing bracket should be in the same column as the first character of the function declaration.

In the same way you indent HTML and CSS, we indent code. To make it clear what lines belong to what block, we indent them a level. Every line that belongs to a block should begin at the same column.

Usually we press the tab key to indent a line. Different editors treat this differently by default.

Some editors type hard tabs: the control character Horizontal Tab (ASCII code 9, \t). This traditionally has been rendered as eight spaces, the same as typewriters would. Some editors will type soft-tabs, substituting \t for eight, four or two actual space characters. Most editors will allow for both, letting you change the setting in the application's preferences.

Aside: The Python programming language actually uses hard tabs to represent code blocks, making indentation an integral part of the code.

As a programmer, your job is to learn the style being used in the code you are working on and work to it. Good code looks like it was written completely by one person, even though it may have been written by a small army over a period of time.

Parameters

function hello (person) {
  console.log("Hello, " + person + "!");
}

Normally, putting parentheses after a name like hello would call a function, but because the keyword function is being used, instead a function will be defined using the name hello. The name person here is actually a variable that will be usable inside the new function. The value of this variable will be the first value passed into the function as an argument. The space before the parentheses is optional, but including it further reinforces to the reader that it is not a call but a declaration.

Optional Parameters

Sometimes, a function will only want a parameter some of the time. If the function is not provided a value for a parameter, the capturing variable will be undefined.

This function says "Hello, (person)", if a parameter value is passed to it. If no value is passed, it will use the value "world" by default.

function hello (person) {
  person = person || "world";
  console.log("Hello, " + person + "!");
}

hello("Dolly"); // "Hello, Dolly!"
hello();        // "Hello, world!"

Here, the Boolean operator for Logical OR serves as a default operator. Technically, the || operator will return the first operand if it's truthy, otherwise it returns the second operand. This behaviour is called short-circuiting&& and || don't actually bother looking at the second operand if the first operand is enough.

When the first parameter is not provided to hello(), person is set to undefined, and undefined is falsy, so || returns "world".

Also note that we don't have to use var person here. Because person is used inside the parentheses in the function definition, the variable is declared and initialised for us.

Return Values

Usually, a function will return a value for use in the code where the function was invoked. To make functions that provide this value back to its caller, we use the return keyword in a statement:

function giveMeALetter(letter) {
  return letter + "!";
}
giveMeALetter("A"); // returns "A!"

When JavaScript executes a return statement, it will stop working through the function and return to the code that called the function. No other code inside the function after the return statement will execute.

function meaningOfLife() {
  console.log("You will see this.");
  return 42;
  console.log("You will never see this.");
}

Aside: While we recommend you always use semi-colons at the end of statements, even though they are optional in JavaScript, JavaScript's Automatic Semi-colon Insertion is the weakest around return statements and can cause problems if you omit them.

What makes a good function?

A function should do one thing only. As a guideline, functions that are more than five statements long should probably be broken up into smaller functions.

A function should be named well. It should do what it says on the label. This builds self-documenting code.

A function should behave reliably. When given input, it should give the same output each time. Except when you intend it to behave randomly, as you might want in a video game.

If a function returns a value, it should not (strictly speaking) have 'side effects' – it should not change any variables outside its scope, or create output.

Example: Calculator

Let's take our coffee order calculator we wrote previously.

var coffeeQuantity = 3;
var pricePerCoffee = 3.60;
var totalPrice = coffeeQuantity * pricePerCoffee;
console.log(coffeeQuantity + " coffees at $" + pricePerCoffee + " each comes to $" + totalPrice);

This is good, but what if our program needs to calculate two or more orders rather than just one?

You might be tempted to copy-paste the code to calculate it twice:

var coffeeQuantity = 3;
var pricePerCoffee = 3.60;
var totalPrice = coffeeQuantity * pricePerCoffee;
console.log(coffeeQuantity + " coffees at $" + pricePerCoffee + " each comes to $" + totalPrice);

coffeeQuantity = 17;
pricePerCoffee = 3.60;
totalPrice = coffeeQuantity * pricePerCoffee;
console.log(coffeeQuantity + " coffees at $" + pricePerCoffee + " each comes to $" + totalPrice);

Note that we don't use var to redeclare the variables, as they are already defined above. You can safely leave the extra vars in, but they are superfluous.

Hopefully you can see here that this will work, but at the cost of making our code harder to maintain. We've duplicated code. Our calculation is now in two different places. Our pricePerCoffee is set twice. And our output string is duplicated too.

Best Practice: D.R.Y. — Don't Repeat Yourself D.I.E. — Duplication Is Evil

If we have to change any one of these things, we now have to change it in two places. This isn't so bad for a small trivial program such as this, but if you imagine a larger program, repeating yourself with code scattered throughout your application, this quickly can become a nightmare to maintain.

D.R.Y. Code is the most important practice of a good programmer. When you find yourself duplicating code, or reaching for copy-paste, this is a sign or code smell that you need to make your code reusable. Functions are the primary way to make code reusable.

Sometimes duplicating code is quicker and easier than writing reusable code, and there's nothing wrong with doing so as long as once you have this piece of code doing what you want it to that you refactor it, or re-write into more reusable code.

var pricePerCoffee = 3.6;
function calculateCoffeeTotal (quantity) {
  return quantity * pricePerCoffee;
}

var coffeeQuantity = 3;
var totalPrice = calculateCoffeeTotal(coffeeQuantity);
console.log(coffeeQuantity + " coffees at $" + pricePerCoffee + " each comes to $" + totalPrice);

var coffeeQuantity = 17;
var totalPrice = calculateCoffeeTotal(coffeeQuantity);
console.log(coffeeQuantity + " coffees at $" + pricePerCoffee + " each comes to $" + totalPrice);

This hasn't reduced the length of our code that much, but now the calculation is happening in one place. If we want to modify that function to also add GST, we can do so:

function calculateCoffeeTotal (quantity) {
  var gst = 1.15;
  return quantity * pricePerCoffee * gst;
}

Note here that there are no magic numbers in the calculation -- every value is documented by its variable name.

We can further reduce duplication by putting our console.log call in a function:

var pricePerCoffee = 3.6;

function calculateCoffeeTotal (quantity) {
  return quantity * pricePerCoffee;
}

function outputCoffeePrice (price, quantity, total) {
  console.log(quantity + " coffees at $" + price + " each comes to $" + total);
}

var coffeeQuantity = 3;
var totalPrice = calculateCoffeeTotal(coffeeQuantity);
outputCoffeePrice(pricePerCoffee, coffeeQuantity, totalPrice);

var coffeeQuantity = 17;
var totalPrice = calculateCoffeeTotal(coffeeQuantity);
outputCoffeePrice(pricePerCoffee, coffeeQuantity, totalPrice);

This function accepts three values as parameters.

Much better! Our code is much more maintainable. If we want to change the output message, we can do it in one place.

Variable Scope

In the coffee calculator above, you may notice that the statement inside the calculateCoffeeTotal function uses a variable that is not defined inside the function. This function is taking advantage of something called variable scope. calculateCoffeeTotal is a global variable as it is not defined inside a function.

In JavaScript, variables are scoped to functions. Variables defined inside functions are said to be locally scoped, and are only accessible to code written within that function, even to functions defined within that function.

Variables not declared in any function are said to be global scoped. This means the variable can be accessed from anywhere, including inside any function. This might sound great, but it is considered bad practice to define global variables.

To see why global variables are bad, consider this example. On a typical web page you may have many different JavaScript programs running. If these programs used global variables, they may unintentionally use the same variable names as each other. This means one variable could be being shared across more than one program. If one program overwrites such a variable, the other program will use the unintended value, which is bad.

Globally-Scoped Variables

var greeting = "hello";
console.log(greeting);

function greet () {
  console.log(greeting);
}

greet();

Because greeting is declared outside of the greet function, the function has access to it.

Locally-Scoped Variables

function greet () {
  var greeting = "hello";
  console.log(greeting);
}

greet();
console.log(greeting); // gives uncaught ReferenceError: greeting is not defined.

In the code above, the variable greeting only exists inside the greet() function. When the function ends, the variable does not remain usable outside the function.

Locally-scoped Variables Obscure Globally-scoped Variables

var message = "bye";

function goodbye () {
  var message = "farewell";
  console.log(message);
}

goodbye();
console.log(message);

In this code, there are two variables called message. One is globally scoped and the other is scoped to goodbye(). They both contain different values.

Because a message variable is being declared inside goodbye(), it obscures access to the global message.

Obscuring is not normally a problem: reusing variable names is normally fine, but if they do conflict with global variables you will have problems.

Best Practice: Always use var when creating variables inside functions to prevent creating global variables.

Hoisting

When a variable is defined in a function, even if the variable is defined in the middle of a code block, the variable is actually being defined before any of the code in that function runs. The variable definition gets hoisted to the top of the function.

function hoistAway() {
  alert(hoist);
  var hoist = 10;
  alert(hoist);
}

is functionally equivalent to:

function hoistAwayEquivelent() {
  var hoist;
  alert(hoist);
  hoist = 10;
  alert(hoist);
}

Both of these functions, when called, will alert "undefined" and then "10".

If you remember to define a variable before you use it, hoisting becomes a non-issue.

Exercises

  • Write a function that returns the input string with French « guillemets » around it. These are not two angle brackets together, but one character.
  • Write a function that returns the input string surrounded with Spanish exclamation marks around it: ¡Arriba!
    • ¿Se puede hacer otra con signos de interrogación españoles?
  • Write a function that takes one number as a parameter, calculates GST, and returns the original number including GST. Write code that uses the function to demonstrate that it works.
  • Write a function that takes a number including GST and returns the number excluding GST.