Functions Inside Functions

At some point, I started feeling OK enough about JavaScript to begin teaching the JavaScript things. This was mostly motivated by the fact that teaching someone really helps me solidify my own knowledge even more. And I also like listening to myself talk. Selfish reasons.

Then at some point, people wanted to start paying me for the teachings of things--classes, private tutoring sessions, etc. Strangers have even reached out to me to blog about their tutorial websites because they think my opinion matters.*
Gif of Gremlin going wow

One of the big topics that students always struggle with is functions. So this blog post, if you haven't noticed from the title, is going to be about function cluster fucks that beginner coders get (rightfully) confused about.

This blog post assumes you've been exposed to functions a tiny bit. This post also only skims the surface but hopefully it provides explanations for concepts people have trouble grasping.

Explaining Functions

I like to think of functions as a set of instructions or a recipe. Sometimes, these instructions require some materials (just like how a recipe requires ingredients).

In JavaScript land, our set of instructions would be the function and our materials would be called arguments (also, parameters---these are used interchangeably).

Functions look like this:

var myFunc = function () {
  // does something
}

or like this:

function myFunc () {
  // does something
}

Example:

var cookieFunction = function (eggs, flour, sugar) {
  // whisk together eggs, flour and sugar
  // put them on a baking sheet
  // bake
}

The eggs, flour and sugar would be the arguments for cookieFunction.

Function definition vs. function invocation

People get this confused ALL THE TIME. The code we have above are what we call function definitions. We are defining the functions.

We are letting the JavaScript engine know that there exists this function that we have written, and it is set to this name or variable.

Using our cookieFunction function as an example...

var cookieFunction = function (eggs, flour, sugar) {
  // whisk together eggs, flour and sugar
  // put them on a baking sheet
  // bake
}

When the JavaScript engine gets to this bit of code above, it DOES NOT RUN.

No cookie is made when the engine sees the code. All the engine does is make a note that there exists a function named cookieFunction. And if it sees cookieFunction in the future, it knows what to do.

How do we make a cookie?

cookieFunction("two eggs", "King Arthur's flour", "brown sugar");

The above is called a function invocation. We are 'invoking a function' here. We can also call this 'running a function' or 'calling a function'. These phrases are interchangeable.

When we run a function, we are telling the engine,

"Hey, you remember that cookieFunction I gave you not long ago? Time to actually go make some cookies!"

In other words, we are telling the engine to execute the function.

To run a function, you need to have the name of the function (or variable it is assigned to) followed by two parentheses:

nameOfYourFunction();

What can functions do?

Functions can do three things.

  • return something

    Functions can return some kind of a value, including other functions (functions are values, too!).

    The function here just returns the number 3.
var three = function () {
  return 3;
} 
  • have [a] side-effect[s]

    Functions can cause something else to happen in the code, outside of itself. It can be one thing or it can be many. It can be something as simple as console logging a value or it can be more complex things, like changing other functions or appending new things to the HTML.

    We call these side-effects.
var three = function () {
  console.log(3);
} 
  • a combo of side-effects AND a return

    A combo of the above.
var three = function () {
  console.log(3);
  return 3;
}

Let's think of functions as a copy machine.

You go to Kinko's to get something copied.

You make the machine copy your paper (it does copies for free!) and it returns you a copy of it that's in your hand.
Likewise, instead of giving you a copy, the machine will scan it and email it to you. That's a side-effect---it is causing a change somewhere else. Scanning also costs you money, so there's another side-effect: you are a few cents poorer.

Or you can do a combo of side-effects and a return. You get emailed a copy, you are a few cents poorer AND you get returned a copy in your hand.

office otter gif

Arguments

A few things about arguments:

  • A function can have as many arguments as it wants
  • Arguments can be anything
  • What you name the arguments need to be clear. If you're expecting a number as your argument, name it num, and not arr.
  • The order that you place them in the function when you invoke it matters
  • Arguments can be mandatory, optional, or a mixture of both. It depends on what needs to be done.
  • Arguments are not variables. They are references FOR the function to what is passed into the function during the function's invocation. (YOWZA).

Let's use some examples.

var hipsterRestaurantNameGenerator = function (instrument, herb) {
  return "The " + instrument + " & the " + herb;
}

We have a function here named hipsterRestaurantNameGenerator and it takes in two strings as arguments. How do we know they're strings? Because inside the function we're concatenating the arguments into one long string.

Also, I wrote it, so I get to decide. hipsterRestaurantNameGenerator takes in two strings---one for a instrument and another for an herb.

How do I run this?

hipsterRestaurantNameGenerator('trumpet', 'kale');

This is going to return me "The trump & the kale". Nice name, huh?

What if we don't put arguments? What would we get?

hipsterRestaurantNameGenerator();

Try running it and see.

Defaulting arguments

Let's say we have this function that takes three string arguments:

var seussLineStarter = function (color1, color2, animal) {
  color2 = color2 || 'blue';
  animal = animal || 'bird';
  return color1 + ' ' + animal + ', ' + color2 + ' ' + animal + ' up on a hill'
} 

If we wanted to run it, it would be something like this:

seussLineStarter('purple', 'yellow', 'goldfish');

We would get back "purple goldfish, yellow goldfish up on a hill".

Inside, the first line checks to see if color2 is provided. If it is provided, it stays as is. If it is not provided, color2 gets set to 'blue'.
The second line does the same thing but for animal. If it is not provided, it defaults to 'bird'.

There are many ways to default your arguments within your function. MDN provides the best reference.

The key things to remember are that arguments default to undefined (unless you set defaults yourself like the above) and the order of arguments matters greatly.

Argument order matters

If we changed around the names of our arguments like so:

var seussLineStarter = function (color2, animal, color1) {
  color2 = color2 || 'blue';
  animal = animal || 'bird';
  return color1 + ' ' + animal + ', ' + color2 + ' ' + animal + ' up on a hill'
} 

but kept the contents of the function the same, running it would result in a totally different line.

seussLineStarter('purple', 'yellow', 'goldfish');

Here, we would instead get back "goldfish yellow, purple yellow up on a hill". Kind of weirdly poetic but not what we're looking for.

This is key because students will often get confused with this: argument names during function definition are simply references to arguments given during function invocation.

You can name them whatever you want. It is the ORDER that arguments are put in (during function invocation) that matters.

Our above sentence is all jumbled because when the engine reads our new seussLineStarter function, it sees color2 and thinks that that's the first argument we provided to seussLineStarter during its invocation.

In this case, it would be 'purple'

Same for animal. The engine thinks animal points to 'yellow', which was the second string we provided seussLineStarter when we invoked it.

Back to our original seussLineStarter with the parameters in the right order:

var seussLineStarter = function (color1, color2, animal) {
  color2 = color2 || 'blue';
  animal = animal || 'bird';
  return color1 + ' ' + animal + ', ' + color2 + ' ' + animal + ' up on a hill'
} 

What if we didn't provide color2? It defaults to 'blue' right?

So we run:

seussLineStarter('purple', 'elephant');

What does this give me? This gives me 'purple bird, elephant bird up on a hill'...not quite what we had in mind.

elephant and bird gif

Why?

Because order matters. When seussLineStarter is invoked, the engine sees only two arguments provided.

color1 ---> 'purple'
color2 ---> 'elephant'

Because animal (in other words, a third parameter) isn't provided, our function defaulted to 'bird'.

Returns

There's a lot that can be said about returns.

  • return is a statement that ends a function
  • When used, it'll specify a specific value to be returned
  • Only expressions can be returned
  • Any code after a return WILL NOT BE RUN
  • You can have multiple returns in a function BUT ONLY ONE WILL WIN. In other words, only one gets to go.

Here we have a pretty stupid function.

var addToThreeIfArgIsTwo = function (num) {
  if (num === 2) {
    return num + 3;
  } else {
    return num;
  }
}

It expects one argument (num) and if that number is 2, it'll return 2 + 3. If the argument is not a 2, it just returns the number.

In it, we have two return statements but you can only reach one.

I also see a lot of students make this mistake:

var doSomething = function (num) {
   return num * num;
   console.log(num * num);
}

Nope. Once you hit the return, the function is over!

Why are returns cool/useful? Why should I care?

BECAUSE IT'S SUPER FUCKING HANDY IS WHY. If you run window.prompt('What is your name') in your console, a prompt window will show up where you can enter some info. But once you press enter, that info flies away into the ether. You never see it again.

YOU NEED TO KEEP IT FOR LATER USINGS!

This is a really basic example but hopefully it helps convey how returns are useful.
Let's say you have a complex function. It takes in Fahrenheit and converts it to Celsius.

var tempConverter = function (f) {
  return Math.round((f - 32) / 1.8)
}

And let's say you're doing a project that always requires you to convert a particular Fahrenheit degree to Celsius. We can say it's 89 degrees Fahrenheit. It comes up over and over again and you always have to run tempConverter---super annoying. Instead of always running it, you can just save it in a variable for use later.

var f89inCelsius = tempConverter(89);

By setting it to a variable, we can store the result for use later. So the next time your project calls to convert 89 Fahrenheit to Celsius, you can just grab this handy little variable and provide that instead.

Where we find functions

Answer: everywhere.
We find them in three forms:

  • the O.G.

    I like to call this the basic function. It stands alone.
var myFunc = function () {
  // doing things
}

// we invoke them by just writing their name and some parentheses

myFunc();
  • the method

    Methods are just functions that find themselves trapped in an object.
var myObj = {
  myFunc: function () {
    // doing things
  }
}

// we have to reference the object in order to access and run them, just like any other key-value pair in objects

myObj.myFunc();
  • the callback

    What most confuses beginners is the callback. When a function is put in another function as an argument, it is a callback.
var myFunc = function (cb) {
  // do things with the cb
  // and maybe some other things
} 

var myCallBack = function () {
  // do other things
}

// by putting myCallback into myFunc, myCallback is not just any regular function now. It's a callback function!

myFunc(myCallback);

Callbacks

Callbacks are function definitions passed into other functions. The key phrase here is function definitions. If it's a function invocation that's passed in, it is no longer a callback, it is whatever that function returns**

Let's say we have a recipe for a cake. In that recipe, we find that we need to also use a recipe the frosting needed to complete the cake. Our cake recipe here is our function. The frosting recipe is the callback.

We must execute the callback before we can finish executing the function. This has to do with something called a call stack. This is something I won't go into during this post.

Students often get confused by these things:

  1. what happens when a function returns a function
  2. invoking the callback inside the function invocation
  3. functions as values
  4. abstraction of anonymous functions into variable names and vice versa

What happens when a function returns a function?

var outerFunction = function () {
  return function () {
    return "I am inside!";
  }
}

Oh man, such confusion on this one for first-timers.

This has to do with seeing functions as values. We tend to think of functions as being these special blocks of code but they are really no different from everything else (except for the fact that they can do extra things).

If we invoke outerFunction, we are simply left with an anonymous function. And the best way to capture that returned function is to set the invocation of outerFunction to some kind of a variable.

var capturedFunc = outerFunction();

Here, capturedFunc is really pointing to the returned function inside of outerFunction.

In other words, these are all equal to the same thing.

capturedFunc ---> outerFunction(); ---> function () { return "I am inside!"; }

When we invoke outerFunction, what takes its place (in other words, what gets returned), is another function definition.

How do we invoke the inner function?

capturedFunc();

Because capturedFunc is really just function () { return "I am inside!"; }
To run it, we just need to invoke it as usual.

Or, if we wanna get RIGHT to it, we can just do this (though it's uglier and not exactly best practice imo).

outerFunction()();

Just keep staring at this:

capturedFunc ---> outerFunction(); ---> function () { return "I am inside!"; }

Invoking the callback inside the function invocation

Meaning if we have two functions, do we pass the callback in as is? Or should we invoke it?

var myFunc = function (cb) {
  return cb();
}

var myCallback = function () {
  return 'Hello!';
}

// Should we just pass it in like this?
myFunc(myCallback);

// or should we invoke it?
myFunc(myCallback());

The first key to this is what happens to the callback inside of myFunc. myFunc simply invokes the callback and then returns it. Because the callback is getting invoked INSIDE the function, we would pass it in as is. No invocation.

What about in this case?

var returnsTwo = function () {
  return 2;
}

var addThree = function (num) {
  return num + 3;
}

// Should we just pass it in like this?
addThree(returnsTwo);

// or should we invoke it?
addThree(returnsTwo());

Again, the answer lays inside our addThree function. The function is expecting some kind of a number to add to 3. In this case, it's not expecting a callback, it's expecting a number.

Even though it looks like we are putting our returnsTwo function inside of addThree as an argument, please be aware that in this case, the argument we are putting in is no longer a callback, it is a number.

The invocation returnsTwo() returns a number. So what we end up putting into addThree is really addThree(2).

Functions as values

Just like objects, arrays, and all the other things, functions are also values.

We access functions very much like how we access objects and arrays.

var myObj = {
  a: 1,
  b: 2
}

var myArr = ['a', 'b'];

var myFunc = function () {
  return 2;
}

Retrieving values from objects and arrays is very similar to running functions:

myObj['a'];
myArr[0];
myFunc();

There's the name and then followed by an open and close punctuation of some sort. In objects and arrays, you're required to put in the key or index.
For functions, you're sometimes expected to give it arguments.

The above really points to other values:

myObj['a'] ---> 1
myArr[0] ---> 'a'
myFunc() ---> 2

Just like how if we have another function that returns a function...

var myOtherFunc = function () {
  return function () {
    console.log("weeeee!");
  }
}

invoking myOtherFunc() is really gonna give us that inner function definition.

myOtherFunc() ---> function () { console.log("weeeee!"); }

Abstraction of anonymous functions into variable names and vice versa

Try to think of ways to take apart functions so that it makes sense to you.

Taking a previous function we made:

var outerFunction = function () {
  return function () {
    return "I am inside!";
  }
}

We can abstract it out into two separate functions and then combine them together for better clarification.

var innerFunction = function () {
    return "I am inside!";
}
var outerFunction = function () {
  return innerFunction;
}

All I did was take that inside function OUT and set it to its own variable.
When we read this, we know that there are two functions---innerFunction and outerFunction and mentally, it makes it a little less intense-looking than what it was before.

What happens when we invoke outerFunction()?

var storedFunc = outerFunction();

Our function storedFunc is really just going to point to innerFunction, because that's just what gets returned.

Same technique for a function that returns a number or a string.

var outerFunction = function () {
  return 'Hello!';
}

// when invoked and set to another variable...
var storedThing = outerFunction();

// we really mean
storedThing --> outerFunction() --> 'Hello!'

Resources

This post should only be regarded as supplementary material to whatever you're using to learn.

* All my blog posts are unaffected by particular ed-tech companies that start with a 'u'

** Unless that function returns another function