These are my solutions to the exercises of the JavaScript track on Exercism.
- Remember to do an initial commit after downloading the exercise.
- Copy over your
.prettier.yaml
file from a previous exercise. - Install any dependencies needed to run the tests. Exercism recommends using pnpm to save disk space (most of the exercises have the same dependencies). Within the exercise directory, run
pnpm install
if you've installedpnpm
with a package manager like Homebrew, orcorepack pnpm install
to let Node.js manage yourpnpm
installation.
- Invoke strict mode by adding
'use strict'
to the start of the solution file. (I'm not sure if this is actually necessary: the solution file is imported as a module by the test script, and modules are always executed in strict mode.) - Once you think you have a working solution, verify with
npm test
.
I often like to write several solutions to an exercise. If I'm only writing multiple implementations for one or two functions, I'll just include the alternatives as (unexported) functions in the same file; the exported function is then just used as a wrapper to select one of the options:
export function requiredFunction(x, y) {
// select an implementation
// return requiredFunction1(x, y)
// return requiredFunction2(x, y)
return requiredFunction3(x, y)
}
If the exercise involves writing a class with a number of methods, I place each implementation in its own file. To select a particular implementation for testing, there are several approaches.
As an example, consider the Linked List exercise. The first line of the testing script, linked-list.spec.js
, attempts to import a class called LinkedList
from a file named linked-list.js
:
import { LinkedList } from './linked-list'
My three solutions to the exercise are in the files original.js
, sentinel.js
and elegant.js
.
-
Option 1. Just symlink the desired solution to
linked-list.js
. This is a fairly robust, language-agnostic approach that works for most of the Exercism language tracks. You may want to add the link target(linked-list.js
in this case) to an exercise-level.gitignore
file. -
Option 2. Replace the first line of the test script as needed. In this case, you'd write something like
// Select an implementation: // import { LinkedList } from './original'; // import { LinkedList } from './sentinel'; import { LinkedList } from './elegant';
-
Option 3. Write a
linked-list.js
file that simple reexports the desired solution:// Select an implementation: // export { LinkedList } from './original'; // export { LinkedList } from './sentinel'; export { LinkedList } from './elegant';
If you want to publish all of your implementations on Exercism, this is the best choice. As with Option 1, you may want to add a
.gitignore
file containinglinked-list.js
.
These are completed during the JavaScript's track's "Learning Mode" to illustrate important language concepts. They're invariably short and simple, and there generally aren't too many reasonable ways to solve them, but they can provide nice examples of JavaScript features and built-in functions. Because each learning exercise is associated with one or more language concepts, I've listed them all below.
null
and undefined
, the optional chaining operator ?.
and the nullish coalescing operator ??
.
Booleans and logical operators.
for
loops and the increment/decrement operators.
Closures, including a nice example of memoization.
Template strings and the ternary operator.
Arrow functions and Array
analysis methods (includes
, indexOf
, every
, some
, find
, findIndex
).
Array destructuring and the rest and spread operators (...
).
Arrays and basic Array
methods (push
, pop
, shift
, unshift
, splice
)
Array iteration options: the for
and for...of
loops and forEach
method.
Apparently, the two loop options are around three times faster than forEach
.
Array transformation methods, both non-mutating (map
, flatMap
, filter
, reduce
) and mutating (reverse
, splice
, sort
).
Inheritance and error handling.
Numbers and arithmetic operators.
Basic callbacks.
Objects.
An introduction to functions. I'm rather fond of my implementation of the quantities
function.
The very first exercise in Learning Mode, covering the basics: assignment with let
and const
; function declarations; exposing a public API with export
.
Type conversion and type coercion, including truthy/falsy values.
while
loops and switch
statements.
Sets. In most programming languages, you can deduplicate an array by converting it to a set and then back again, but the syntax is especially nice in JavaScript.
Recursion. The orderPrice
function explores stack overflow: one of the test cases is so large that a recursive solution will exceed the maximum call stack size. I wrote a tail-recursive implementation of orderPrice
as well, but this doesn't solve the problem: tail call optimization is part of the ECMAScript 6 specification, but the V8 JavaScript engine (and hence Node.js) does not implement it.
Strings and string methods.
Regular expressions.
Promises.
Conditionals (if
statements) and comparisons. Although it's jumping ahead a little, I completed this exercise with the ternary operator (?:
) instead of if
statements. In general, I prefer to use the ternary operator whenever I can: unlike if
and switch
statements, it returns an expression that can be directly assignment to a variable or returned from a function.
Classes and methods defined using both the older prototype syntax and the newer class syntax.
These are meant for students who've completed Learning Mode or otherwise acquired basic language proficiency, and vary considerably in length and difficulty. Unlike the learning exercises, the list below is not exhaustive.
More regular expression practice. Also, switch (true)
can be a succinct alternative to an if...else
chain.
A tricky problem that can be solved with recursion. Array
transformation methods (map
, filter
, reduce
, sort
) allow for a fairly concise solution.
A simple introduction to Date
objects.
There's a very nice, functional implementation of the grade
method. It's a bit of a judgment call as to whether the #students
field should be a simple object or a Map
.
I chose to store students as keys and grades as values, which makes the add
method short and efficient, but the roster
and grade
methods slower and more involved. Most community solutions went with the opposite approach.
A classic doubly linked list, suitable for use as a deque. I wrote several versions, with and without sentinel nodes. This exercise presents a perfect opportunity to use private fields and methods, which are relatively recent additions to the language.
Four possible solutions using Array
methods, a Set
, or a bit field.