Template Literals and First Class Citizens

String Interpolation with Template Literals

As we dig deeper into HTML-in-JavaScript, you'll notice that standard String concatenation gets to be a bit cumbersome. Instead of using the concatenation operator (+), most String manipulation in modern JS is done with template literals and String interpolation. The idea is that we can inject variables or expressions directly into a template, rather than piecing together substrings manually. The template literal version of the exercise above would look like this:

const firstName = prompt("Hi there! What's your first name?")";
const lastName = prompt("What's your last name?")";
const output = document.querySelector("#greeting");

output.innerHTML = `<p>Thanks for visiting, ${firstName} ${lastName}.</p>`;

Notice how this syntax maintains the structure of our HTML more accurately. This is true of multi-line HTML chunks as well, since template strings take newlines into account! If we wanted to make our output's innerHTML' a bit more fancy, we could have re-written that last line like so:

output.innerHTML = `
  <div>
    <p>
      Thanks for visiting,
      <span class="some-class">
        ${firstName} ${lastName}
      </span>
    </p>
  </div>
`;

Which is much nicer, don't you think? You can imagine how we might be able to turn all of our existing HTML into a set of fancier templates using this method. Try refactoring all of your concatenated Strings into interpolated template literals instead!

Fun With Functions

Review

We've already seen a variety of functions, from built-in functions like console.log(), to a few of our own built using function functionName() {}. We've also already learned how to invoke or execute functions with (), as well as passing in a few simple arguments (like we've done with prompt("some string")).

function sayingGenerator() {
  const phrase = "Heeey, it's the Fonz.";
  return phrase;
}

// What is the return value?
const saying = sayingGenerator();

function valueLogger(value) {
  console.log(value);
}

valueLogger("Howdy ho, neighborino!");

// parameters and variables defined in function invocations are local to that invocation
value; // ReferenceError: No variable 'value' exists

valueLogger(3 + 7);

// where's the seven?
valueLogger(3, 7);

function doubler(num) {
  return num * 2;
}

// is it ten?
const shouldBeTen = doubler(5);

function doubleValueLogger(value1, value2) {
  console.log(value1, value2);
}

doubleValueLogger("hello", "how are you");

// what is value2?
doubleValueLogger("hello");

function add(num1, num2) {
  return num1 + num2;
}

const sum = add(7, 12);

a new syntax (part 1)

Up to this point, we've been writing functions like this:

const someFunction = function someFunction() {};

While there are some technical differences between the two ways of declaring functions, the latter is used much more widely than the former, and is generally a bit easier to read. As long as we treat the functional expression the same way we were treating it's const-based cousin, we should be fine (and save ourselves a few keystrokes in the process).

the return keyword

Here's a function that returns a value without any side effects, meaning that invoking the function will have no affect anything outside of that function's _scope. (nothing outside of the { and }).

function greeter() {
  return "Hello";
}

// saving the return value
const greeting = greeter();

// using the return value to compose larger expressions
console.log(`${greeting}, nice to meet you.`);

// what's the difference here?
console.log(`${greeter()}, nice to meet you.`);

function tripler(num) {
  return num * 3;
}

function multiply(num1, num2) {
  return num1 * num2;
}

function divide(num1, num2) {
  return num1 / num2;
}

function remainder(num1, num2) {
  return num1 % num2;
}

// TODO: Using only the functions above, and no operators, calculate the value of tripling 5, multiplying that by 12, dividing by 2 and then finding the remainder of dividing that by 3.

Functions are First Class Citizens

Functions are ‘first-class citizens’ afforded all of the rights and privileges of other data types. This means that they can be assigned as variables (function declarations - like variable declarations):

const greeter = function() {
  return 'hello world'`
  }

More importantly, they can be passed into other functions as arguments!

function adder(1, 2) {
  return 1 + 2;
}

/**
  * `doMath` is a HIGHER ORDER FUNCTION.
  * `operation` is passed in as an argument (as any other data type could be).
  * Whichever function is passed in as `operation`,
  * is INVOKED by 'doMath`
  */
function doMath(operation, num1, num2) {
  return operation(num1, num2);
}

Last updated

Was this helpful?