Skip to main content
This appendix aims to give you some challenging and interesting exercises to test and solidify your understanding of the main topics from this book.
It’s a good idea to try out the exercises yourself—in an actual code editor!—instead of skipping straight to the solutions at the end. No cheating!
These exercises don’t have a specific right answer that you have to get exactly. Your approach may differ some (or a lot!) from the solutions presented, and that’s OK.

Buckets of Marbles

This exercise asks you to write a program—any program!—that contains nested functions and block scopes, which satisfies these constraints:
You can just write junk foo/bar/baz-type code for this exercise, but I suggest you try to come up with some sort of non-trivial real’ish code that does something kind of reasonable.
Try the exercise for yourself, then check out the suggested solution at the end of this appendix.

Closure (PART 1)

Let’s practice closure with some common computer-math operations: determining if a value is prime, and generating a list of prime factors. For example:
Here’s an implementation of isPrime(..):
And here’s a basic implementation of factorize(..):
If you call isPrime(4327) multiple times in a program, it goes through all its computation steps every time. That’s a lot of wasted work!

Part 1

Use closure to implement a cache to remember the results of isPrime(..), so that the primality of a given number is only ever computed once.

Part 2

Use the same closure cache technique for factorize(..).
Use separate closures for caching of isPrime(..) and factorize(..).

A Word About Memory

We can see that saving repeated calls improves computation speed. But this usage of closure is making an explicit trade-off: memory.
We’re essentially growing our cache (in memory) unboundedly. If the functions were called many millions of times with mostly unique inputs, we’d be chewing up a lot of memory.
This can definitely be worth the expense, but only if we think it’s likely we see repetition of common inputs.
It might be a good idea to have a more sophisticated caching approach, such as an LRU (least recently used) cache, that limits its size. As it runs up to the limit, an LRU evicts the values that are least recently used.
Try the exercise for yourself, then check out the suggested solution at the end of this appendix.

Closure (PART 2)

In this exercise, we’re going to practice closure by defining a toggle(..) utility that gives us a value toggler. You will pass one or more values (as arguments) into toggle(..), and get back a function. That returned function will alternate/rotate between all the passed-in values in order, one at a time, as it’s called repeatedly.
The corner case of passing in no values to toggle(..) is not very important; such a toggler instance could just always return undefined.
Try the exercise for yourself, then check out the suggested solution at the end of this appendix.

Closure (PART 3)

In this third and final exercise on closure, we’re going to implement a basic calculator. The calculator() function will produce an instance of a calculator that maintains its own state:
Each time calc(..) is called, you’ll pass in a single character that represents a keypress of a calculator button. We’ll restrict our calculator to:
  • Digits (0-9)
  • Arithmetic operations (+, -, *, /)
  • ”=” to compute the operation
Operations are processed strictly in the order entered; there’s no ”( )” grouping or operator precedence. For example:
Here’s a useCalc(..) helper that runs the calculator with characters from a string:
Here’s a formatTotal(..) function your calculator should use:
Don’t get too mired in calculator-specific behavior. Focus on the memory of closure.
Try the exercise for yourself, then check out the suggested solution at the end of this appendix.

Modules

This exercise is to convert the calculator from Closure (PART 3) into a module. We’re not adding any additional functionality, only changing its interface. Instead of calling a single function calc(..), we’ll be calling specific methods on the public API for each “keypress.” This module should be expressed as a classic module factory function called calculator(), so that multiple calculators can be created if desired. The public API should include:

number()

Input: the character/number “pressed”

Operators

plus(), minus(), mult(), div()

eq()

Compute the result
Usage would look like:
Here’s the updated useCalc(..) helper:
Try the exercise for yourself, then check out the suggested solution at the end of this appendix.
As you work on this exercise, spend some time considering the pros/cons of representing the calculator as a module as opposed to the closure-function approach from the previous exercise.BONUS: Write out a few sentences explaining your thoughts.BONUS #2: Try converting your module to other formats: UMD, CommonJS, and ESM.

Suggested Solutions

Remember, each suggested solution is just one way to approach the problems. They’re not “the right answer,” but they do illustrate a reasonable approach.

Suggested: Buckets of Marbles

Suggested: Closure (PART 1)

The general steps:
  1. Wrap an IIFE to define the scope for the cache variable
  2. In the underlying call, first check the cache
  3. At each return, assign to the cache and return that result

Suggested: Closure (PART 2)

Suggested: Closure (PART 3)

Suggested: Modules


That’s it for this book! Congratulations on your achievement. When you’re ready, move on to the next book in the series.