- 3.1 Declaring Functions
- 3.2 Higher-Order Functions
- 3.3 Function Literals
- 3.4 Arrow Functions
- 3.5 Functional Array Processing
- 3.6 Closures
- 3.7 Hard Objects
- 3.8 Strict Mode
- 3.9 Testing Argument Types
- 3.10 Supplying More or Fewer Arguments
- 3.11 Default Arguments
- 3.12 Rest Parameters and the Spread Operator
- 3.13 Simulating Named Arguments with Destructuring
- 3.14 Hoisting
- 3.15 Throwing Exceptions
- 3.16 Catching Exceptions
- 3.17 The finally Clause
- Exercises
Exercises
What does the indexOf function of Section 3.1, “Declaring Functions” (page 51), do when an object is passed instead of an array?
Rewrite the indexOf function of Section 3.1, “Declaring Functions” (page 51), so that it has a single return at the end.
Write a function values(f, low, high) that yields an array of function values [f(low), f(low + 1), . . ., f(high)].
The sort method for arrays can take an argument that is a comparison function with two parameters—say, x and y. The function returns a negative integer if x should come before y, zero if x and y are indistinguishable, and a positive integer if x should come after y. Write calls, using arrow functions, that sort:
An array of positive integers by decreasing order
An array of people by increasing age
An array of strings by increasing length
Using the “hard objects” technique of Section 3.7, “Hard Objects” (page 59), implement a constructCounter method that produces counter objects whose count method increments a counter and yields the new value. The initial value and an optional increment are passed as parameters. (The default increment is 1.)
const myFirstCounter = constructCounter(0, 2) console.log(myFirstCounter.count()) // 0 console.log(myFirstCounter.count()) // 2
A programmer thinks that “named parameters are almost implemented in JavaScript, but order still has precedence,” offering the following “evidence” in the browser console:
function f(a=1, b=2){ console.log(`a=${a}, b=${b}`) } f() // a=1, b=2 f(a=5) // a=5, b=2 f(a=7, b=10) // a=7, b=10 f(b=10, a=7) // Order is required: a=10, b=7
What is actually going on? (Hint: It has nothing to do with named parameters. Try it in strict mode.)
Write a function average that computes the average of an arbitrary sequence of numbers, using a rest parameter.
What happens when you pass a string argument to a rest parameter ...str? Come up with a useful example to take advantage of your observation.
Complete the mkString function of Section 3.13, “Simulating Named Arguments with Destructuring” (page 66).
The archaic var keyword interacts poorly with closures. Consider this example:
for (var i = 0; i < 10; i++) { setTimeout(() => console.log(i), 1000 * i) }
What does this code snippet print? Why? (Hint: What is the scope of the variable i?) What simple change can you make to the code to print the numbers 0, 1, 2, . . . , 9 instead?
Consider this declaration of the factorial function:
const fac = n => n > 1 ? n * fac(n - 1) : 1
Explain why this only works because of variable hoisting.
In sloppy (non-strict) mode, functions can be declared inside a nested block, and they are hoisted to the enclosing function or script. Try out the following example a few times:
if (Math.random() < 0.5) { say('Hello') function say(greeting) { console.log(`${greeting}!`) } } say('Goodbye')
Depending on the result of Math.random, what is the outcome? What is the scope of say? When is it initialized? What happens when you activate strict mode?
Implement an average function that throws an exception if any of its arguments is not a number.
Some programmers are confused by statements that contain all three of try/catch/finally because there are so many possible pathways of control. Show how you can always rewrite such a statement using a try/catch statement and a try/finally statement.