JavaScript Refresh – Functions

Functions help us structure larger programs, reduce repetition, self-document pieces of a program using names, and isolate the code from other parts.

A new function is like adding a new word to a language.

Defining a function

The function is the value of the variable.

const double = function (x) {
    return x * 2;
};

The value of double is the function.

function parts

  • function keyword
  • parameters (optional)
  • curly braces (sometimes required)

The body contains the statements that are executed when the function is called.

The parameters are like normal variables, but their values are assigned when the function is called.

Two function types

  • Side affect functions
  • Value returning function

Bindings and Scopes

Variables are only visible in certain parts of a program.

Global bindings are variables defined outside of a function or block.

Local bindings (parameters & variables declared in a function or block) are only visible in the function or block.

The let and const keywords create local variables in blocks and functions. The var keyword only creates local variables in functions.

Scopes can look outward.

let x = 10;
if (true) {
  let y = 20;
  var z = 30;
  console.log(x + y + z);
  // → 60
}
// y is not visible here
console.log(x + z);
// → 40

Nested scope

In JavaScript, you can create blocks and functions inside of blocks and functions.

All scopes can see the global scope.

Lexical scoping

const hummus = function(factor) {
  const ingredient = function(amount, unit, name) {
    let ingredientAmount = amount * factor;
    if (ingredientAmount > 1) {
      unit += "s";
    }
    console.log(`${ingredientAmount} ${unit} ${name}`);
  };
  ingredient(1, "can", "chickpeas");
  ingredient(0.25, "cup", "tahini");
  ingredient(0.25, "cup", "lemon juice");
  ingredient(1, "clove", "garlic");
  ingredient(2, "tablespoon", "olive oil");
  ingredient(0.5, "teaspoon", "cumin");
};

Declaration Notation

function double (x) {
    return x * 2;
}

Declaring a function like this automatically creates a variable called double for the function.

You don’t need a semi-colon after the closing curly brace when creating a function like this.

These types of functions are hoisted to the top of the scope. You can call them before they are written.

Arrow Function

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

If there is only one parameter, we can exclude the parenthesis.

const double = x => { return x * 2; }

If there is only one statement, you can even remove the curly braces and remove the return keyword.

const double = x => x * 2;

The Call Stack

function greet(person) {
    console.log('hello, ' + person);
}
greet("John");

The control jumps to the function when it’s called. When a function is done running, control jumps out of a function and gives the returned value to the context in which the function was called.

The computer has to remember the context of the place where a function was called. It has to remember where to go when it’s done running.

Each time a function is called, the context gets saved to the call stack. When the function returns, it looks at the most recent entry in the stack to remember where it should pick up again.

Optional Arguments

You can pass too many or too few arguments, and JavaScript doesn’t care.

function multipy (x, y) {
    return x * y;
}
multiply (3, 4, 8);

You can set default arguments.

function multiply (x, y=2) {
    return x * y;
}
multiply(2);

Closure

Local bindings are created anew each time a function is called.

In JS, functions are treated like values, and local bindings are re-created each time a function is called.

That means you can access a specific instance of a local binding in an enclosing scope. This ability or feature is known as closure. You don’t have to worry about the lifetime of bindings.

function wrapValue(n) {
  let local = n;
  return () => local;
}

let wrap1 = wrapValue(1);
let wrap2 = wrapValue(2);
console.log(wrap1());
// → 1
console.log(wrap2());
// → 2

Recursion

Functions can call themselves. You can use loops in place of recursive functions, and loops are 3x faster. If a function calls itself too any times, it can overflow the call stack.

Growing Functions

Don’t add cleverness unless you’re absolutely sure you’ll need it. It can be tempting to create general frameworks for every piece of functionality.

Functions and Side Effects

Some functions return a value and some functions have side effects.

A pure function returns a value and doesn’t rely on side affects from other code. For example, it doesn’t need global variables whose value might change.