Course outline · 0% complete

0/29 lessons0%

Course overview →

Declaring functions

lesson 6-1 · ~9 min · 18/29

Packaging work under a name

Lesson 5-3 covered the call that turns text such as '{"a":1}' into a live object you can read with dots: JSON.parse(text). Its partner JSON.stringify goes the other way, from object to text.

Parsing incoming data is something programs do constantly, and it rarely stands alone. It comes bundled with validation, defaults, and error handling, which is far too much to repeat everywhere it is needed. This unit covers the tool for that problem, giving a chunk of work a name so it can be written once and called anywhere.

def becomes function

Functions remain the unit of reuse in any language: name a computation once, call it everywhere, test it in isolation. Every JavaScript codebase you will ever read is mostly function definitions, so this unit makes writing them automatic.

A Python function:

def greet(name):
    return f"Hello, {name}!"

The same function in JavaScript:

function greet(name) {
  return `Hello, ${name}!`;
}

The pieces map one-to-one: the keyword is function, parameters sit in parentheses, the body sits in braces, and return sends a value back. Calling is identical: greet("Ada").

Default parameter values work like Python too: function area(w, h = 2) uses 2 whenever h is not passed. One difference to know: calling with too few arguments does not raise an error like Python's TypeError. The missing parameter is simply undefined, so bugs surface later rather than immediately.

A function with no return (or a bare return;) gives back undefined, the same role as Python's None.

A greeting and a default parameter

Two small functions, each demonstrating one idea. greet builds and returns a string, and area shows what a default parameter does when an argument is left out.

function greet(name) {
  return `Hello, ${name}!`;
}

function area(w, h = 2) {
  return w * h;
}

console.log(greet("Ada"));
console.log(area(5, 3));
console.log(area(5));

Output

Hello, Ada!
15
10

The two area calls are the pair to compare. area(5, 3) supplies both arguments and multiplies 5 × 3. area(5) leaves h out entirely, so the default of 2 fills in and the result is 10. A default only applies when the argument is absent, and it can be any expression, not just a literal.

Note that greet produces its string with return rather than printing it. That separation is deliberate and worth copying: a function that returns a value can be printed, stored, or passed along, while a function that prints can only ever print.

Converting Celsius to Fahrenheit

A conversion formula is the cleanest kind of function, taking one number in and giving one number back with no side effects at all.

function celsiusToF(c) {
  return c * 9 / 5 + 32;
}

console.log(celsiusToF(0));
console.log(celsiusToF(100));

Output

32
212

The two calls check the formula at both ends of the familiar scale, freezing and boiling, which is a good habit when a function encodes a formula. The expression relies on ordinary precedence: multiplication and division happen before the addition, so c * 9 / 5 is computed first and 32 is added to the result.

Defining the function costs nothing on its own. Nothing runs until a call happens, which is why the output comes entirely from the two console.log lines.

Returning early

return does two jobs at once: it hands back a value and stops the function immediately. Professionals lean on the second job to reject special cases at the top, where a check-and-return pair is called a guard clause. The main logic below then stays flat instead of nesting inside else after else:

function describe(age) {
  if (age < 0) {
    return "Invalid age";
  }
  if (age < 18) {
    return "Minor";
  }
  return "Adult";
}

Once a return runs, nothing after it in the function does. No else is needed here, because merely reaching the second if already proves the age was not negative. Each guard narrows the possibilities for everything below it, so the final return "Adult" needs no condition at all.

Validating a username with two guards

Two guard clauses handle the rejections, and the ordinary case falls through to the bottom. Three calls exercise all three outcomes.

function checkUsername(name) {
  if (name.length < 3) {
    return "Too short";
  }
  if (name.includes(" ")) {
    return "No spaces allowed";
  }
  return "OK";
}

console.log(checkUsername("al"));
console.log(checkUsername("ada lovelace"));
console.log(checkUsername("ada"));

Output

Too short
No spaces allowed
OK

"al" fails the length guard and returns immediately, so the space check never runs on it. "ada lovelace" passes the length guard, then trips the second one. "ada" passes both and reaches the final line.

includes appeared in lesson 4-2 as an array method, and it works on strings too, answering true when the given text appears anywhere inside. Sharing a method name across arrays and strings is common in JavaScript, and indexOf and slice behave that way as well.

A function with no return

Given function ping() { console.log("ping"); }, the call console.log(ping()) prints ping first and then undefined, because the function has no return.

ping() runs, prints, and hands back nothing, and "nothing" in JavaScript is the value undefined. The outer console.log receives that value and prints it. Python behaves the same way, except its name for the value is None. To get a real value out, the function has to return it.