1.1. Classes as first-class objects: We learned that functions are first-class objects, but did you know that classes also are? (Though, of course, speaking of classes as objects does sound weird.) Look at the following example and see what makes it tick! Be careful: there's some purposefully weird code in it:
const makeSaluteClass = term =>
class {
constructor(x) {
this.x = x;
}
salute(y) {
console.log(`${this.x} says "${term}" to ${y}`);
}
};
const Spanish = makeSaluteClass("HOLA");
new Spanish("ALFA").salute("BETA");
// ALFA says "HOLA" to BETA
new (makeSaluteClass("HELLO"))("GAMMA").salute("DELTA");
// GAMMA says "HELLO" to DELTA
const fullSalute = (c, x, y) => new c(x).salute(y);
const French = makeSaluteClass("BON JOUR");
fullSalute(French, "EPSILON", "ZETA");
// EPSILON says "BON JOUR" to ZETA
1.2. Factorial errors: Factorials, as we defined them, should only be calculated for non-negative integers; however, the function that we wrote in the Recursion section doesn't verify whether its argument is valid. Can you add the necessary checks? Try to avoid repeated, redundant tests!
1.3. Climbing factorial: Our implementation of a factorial starts by multiplying by n, then by n-1, then n-2, and so on in what we could call a downward fashion. Can you write a new version of the factorial function that will loop upwards?
1.4. Code squeezing: Not that it's a goal in itself, but by using arrow functions and some other JavaScript features, you can shorten newCounter() to half its length. Can you see how?