First-class functions, pass by value and undeclared variables

First-class functions, pass by value and undeclared variables
Photo by Emile Perron / Unsplash

I wrote this for myself to gain a better grasp of these topics while studying for Launch School. I'm sharing it here because it might be helpful for you too!

Functions as objects and how they can be passed around like regular values.

In JavaScript, functions are first-class functions. This means they can be treated like any other value. Practically speaking, this means they can be assigned to a variable, passed to a function as an argument, and returned by other functions. The below code snippet shows how a function can be assigned to a variable:

function counter(num) {
  let newNum = num + 1;
  return newNum;
}

let newCounter = counter;

console.log(newCounter(5)); // outputs 6

Even though the function declaration assigns counter as the name of the function, by declaring variable newCounter and initializing it to the counter function, we can use variable newCounter to invoke the function. This concept is also what enables function expressions, as a function expression immediately assigns a function to a variable:

let newCounter = function(num) {
  let newNum = num + 1;
  return newNum;
};

Using the counter function from the first example, we can also see how it can be passed to another function as an argument. A function passed to another function as an argument is called a call back function. A function that takes another function as an argument is called a higher-order function:

const ORIGINAL_NUM = 7;

function counter(num) {
  let newNum = num + 1;
  return newNum;
}

function counterResult(firstNum, count) {
  console.log(`The number ${count(firstNum)} is larger than ${ORIGINAL_NUM}.`);
}

counterResult(ORIGINAL_NUM, counter); // outputs “The number 8 is larger than 7.”

In this code snippet, function counter is a call back function, as it is passed as an argument to function counterResult, making counterResult a higher-order function. Finally, functions can also be returned by other functions, as shown in this code snippet:

function favouriteCountry(country) {
  return function(city) {
	console.log(`My favourite city, ${city}, is in ${country}.`);
  };
}

let favouriteCity = favouriteCountry('the Netherlands');

favouriteCity('Amsterdam'); // outputs "My favourite city, Amsterdam, is in the Netherlands."

In this code snippet, function favouriteCountry returns a function that takes a single argument, and then logs a template literal to the console. By declaring and initializing the variable favouriteCity to the function favouriteCountry with an argument passed in, we can then invoke inner function and pass it the required argument.

The terms 'pass by value' and 'pass by reference' and the situations in which their use can be applied.

Pass by value and pass by reference are both concepts that apply to function arguments. It refers to what a function is able to do to the value that is passed to it. JavaScript uses both concepts, but they are applied in different scenarios. Which concept applies depends on the value that is passed as an argument. The key differentiator is whether the passed value can be mutated. Primitive values can never be mutated, so when a primitive value is passed, JavaScript acts like it is using pass by value. Most objects can be mutated, so when an object is passed, JavaScript acts like it is using pass by reference.

Pass by value means that a function cannot change anything about the original value of a variable. This always applies to primitive values, as these are immutable. While a function can reassign a variable to a new primitive value, the original primitive value of the variable cannot be changed. For example:

let petName = "Joey";

function newPetName(pet) {
  let newName = pet;
  newName = "Budgie";
  console.log(newName);
}

newPetName(petName); // outputs Budgie
console.log(petName); // outputs Joey

In this code snippet, the variable petName is declared and initialized to the string ”Joey” on line 1. On line 9, the newPetName function, declared on lines 3 to 7, is called and passed the variable petName as an argument. The newPetName function declares the variable newName and initializes it to the value of the argument passed to it. Then, the variable newName is reassigned to the string ”Budgie” and logged to the console. On line 10, the variable petName is logged to the console, outputting its original string ”Joey”. This demonstrates that the value passed to the function was a copy of the original value. The newName variable does not point to the same point in memory as the petName variable.

Pass by reference means that a function can change something about the original value of a variable. This always applies to objects, as (most of) these are mutable. For example:

let petNames = ["Joey", "Budgie", "Max"];

function newPetName(pets) {
  pets[1] = "Barkie";
  console.log(pets);
}

newPetName(petNames); // outputs ['Joey', 'Barkie', 'Max']

In this code snippet, the variable petNames is declared and initialized to an array containing three string values: “Joey”, “Budgie” and “Max”. The newPetName function, declared on lines 3 to 6, takes one array as an argument, mutates the value at index 1 of the array and assigns it the string “Barkie”. Then, the array is logged to the console. When the newPetName function is called on line 8 and passed the petNames array, it reassigns the value of the element at index 1, “Budgie”, to ”Barkie”. The array is then printed to the console. This demonstrates that the value passed to the function is a pointer to the same place in memory as the petNames variable. As a result, the change made to the array by the newPetName function also affected the original array.

Undeclared variables

A variable is normally declared using the keyword let, and optionally initialized with a value. A constant  is normally declared using the keyword const. If a variable is not declared using let or const, it becomes an undeclared variable. Undeclared variables have the unique characteristic that they always exist on the global scope. This can lead to unexpected behavior. For example, if a function contains an undeclared variable, that variable can now be changed by any part of your program. The following example shows how an undeclared variable is always accessible on the global scope:

let dinner = "broccoli";

function worstMeal(ingredient) {
  condiment = "mustard";
  return (`${ingredient} with ${condiment} is a horrible combination`);
}

console.log(worstMeal(dinner)); // outputs “broccoli with mustard is a horrible combination”
console.log(condiment); // outputs “mustard”

The variable condiment is first used on line 4 within the function scope of the worstMeal function and is successfully logged to the console on line 9, even though it is never declared. Had the variable condiment been declared within the worstMeal function, it would not have been accessible on the global scope, and the code would have returned a ReferenceError.

The difference between an expression and a statement

Expressions and statements are two fundamental concepts in JavaScript, but it can be tricky to remember the differences between the two. There are a number of ways to distinguish between statements and expressions:
You can’t capture a value from a statement
Statements control the flow of a program
Expressions can be part of a statement, but not all statements can be part of an expression

Expressions can evaluate to a value, and that value can be captured and used. Constants, function calls and variables are all examples of expressions. An expression can look like this:

console.log(4 + 3);

Statements, on the other hand, do not allow you to capture a value. A statement is any syntactic unit of code that expresses an action for the computer to perform. For loops, variable declarations and variable assignments are all examples of statements. A statement can look like this:

let height = 78;

A variable declaration like this does not evaluate to a value. After it has been declared, however, the variable height is an expression, and evaluates to the number 78.