A Drip of Javascript Book Sample
A Drip of Javascript Book Sample
A Drip of Javascript Book Sample
Joshua Clanton
This book is for sale at http://leanpub.com/a-drip-of-javascript-book
This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you do.
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Bug Reports & Feature Requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Credits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Resources
In addition to the newsletter itself¹, there is also an official Drip Discussion Group² for readers to
talk about the concepts found in each drip.
Unfortunately, this construct isn’t available in current versions of JavaScript. So what can we do to
achieve this behavior with our current set of tools?
The simplest solution looks like this:
This relies on the fact that a parameter omitted at call time is always undefined. And it’s a good
solution if you have only one parameter to deal with. But what if you have several?
Well if you have more than a few parameters, you should probably be passing in an object parameter,
as that has the advantage of explicitly naming everything. And if you’re passing in an object
parameter, it makes sense to declare your defaults the same way.
⁶https://brendaneich.com/2012/10/harmony-of-dreams-come-true/
Default Parameters in JavaScript 4
1 function myFunc(paramObject) {
2 var defaultParams = {
3 param1: "first string",
4 param2: "second string",
5 param3: "third string"
6 };
7
8 var finalParams = defaultParams;
9
10 // We iterate over each property of the paramObject
11 for (var key in paramObject) {
12 // If the current property wasn't inherited, proceed
13 if (paramObject.hasOwnProperty(key)) {
14 // If the current property is defined,
15 // add it to finalParams
16 if (paramObject[key] !== undefined) {
17 finalParams[key] = paramObject[key];
18 }
19 }
20 }
21
22 console.log(finalParams.param1,
23 finalParams.param2,
24 finalParams.param3);
25 }
That’s a little unwieldy, so if you’re using this pattern a lot, it makes sense to extract the iteration and
filtering logic into its own function. Fortunately, the clever folks who write jQuery⁷ and Underscore⁸
have done just that with their respective extend methods.
Here’s an updated version which uses Underscore’s extend to achieve the same result.
⁷http://api.jquery.com/jQuery.extend/
⁸http://underscorejs.org/#extend
Default Parameters in JavaScript 5
1 function myFunc(paramObject) {
2 var defaultParams = {
3 param1: "first string",
4 param2: "second string",
5 param3: "third string"
6 };
7
8 var finalParams = _.extend(defaultParams, paramObject);
9
10 console.log(finalParams.param1,
11 finalParams.param2,
12 finalParams.param3);
13 }
14
15 // Outputs:
16 // "My own string" and "second string" and "third string"
17 myFunc({param1: "My own string"});
And that’s how you can get default parameters in current versions of JavaScript.
Transforming Arrays with Array#map
One of the most common tasks that developers perform in any language is taking an array of values
and transforming those values. Up until recently, doing that in JavaScript took a fair bit of boilerplate
code. For instance, here is some code for darkening RGB colors:
1 var colors = [
2 {r: 255, g: 255, b: 255 }, // White
3 {r: 128, g: 128, b: 128 }, // Gray
4 {r: 0, g: 0, b: 0 } // Black
5 ];
6
7 var newColors = [];
8 var transformed;
9
10 for (var i = 0; i < colors.length; i++) {
11 transformed = {
12 r: Math.round( colors[i].r / 2 ),
13 g: Math.round( colors[i].g / 2 ),
14 b: Math.round( colors[i].b / 2 )
15 };
16
17 newColors.push(transformed);
18 }
19
20 // Outputs:
21 // [
22 // {r: 128, g: 128, b: 128 },
23 // {r: 64, g: 64, b: 64 },
24 // {r: 0, g: 0, b: 0 }
25 // ];
26 console.log(newColors);
As you can see, there’s quite a bit going on in that code that isn’t really about what we want to
accomplish, but is keeping track of trivial things like the current index and moving the values into
the new array. What if we didn’t have to do all of that?
Fortunately in ECMAScript 5 (the latest version of JavaScript), we don’t. Here is the same example
rewritten to take advantage of the map method:
Transforming Arrays with Array#map 7
Much nicer isn’t it? Invoking map returns a new array created by running a transformation function
over each element of the original array.
Now the only thing you need to keep track of is the logic of the transformation itself.
Of course, map isn’t limited to simple transformations like this. Your function can also make use of
two additional parameters, the current index and the array itself. Consider the following example:
As you can see, the additional parameters make it easy to create transformation functions which use
the array element’s neighbors. This can be useful in implementing something like Conway’s Game
of Life⁹.
Browser support for map is pretty good, but not universal. It isn’t supported in IE 8 and below. You
have a few options for dealing with this.
One of the most powerful techniques for avoiding programming bugs is to reduce the number of
things that you are keeping track of manually. Array’s map method is one more tool to help you do
exactly that.
¹⁰https://github.com/kriskowal/es5-shim
¹¹http://underscorejs.org/#map
¹²http://lodash.com/docs#map
Dealing with the Dangers of this in
Constructors
As I mentioned in the previous drip, invoking a constructor without new can be dangerous. Let’s go
over why.
1 function Color(r, g, b) {
2 this.r = r;
3 this.g = g;
4 this.b = b;
5 }
6
7 // Safe invocation
8 var red = new Color(255, 0, 0);
9
10 // Dangerous invocation
11 var blue = Color(0, 0, 255);
When a constructor is invoked with the new keyword, the this value of the constructor is set to
the new object that you are creating. But when a constructor is invoked like a normal function, its
this defaults to the same this variable that any other function gets. And normally that is the global
object (window in the browser.)
Here is an illustration of the problem.
1 // Global variable
2 r = "Rodent Of Unusual Size";
3
4 function Color(r, g, b) {
5 this.r = r;
6 this.g = g;
7 this.b = b;
8 }
9
10 // Dangerous invocation
11 // Means `this` is the global object
12 var blue = Color(0, 0, 255);
13
Dealing with the Dangers of this in Constructors 10
14 // Outputs: 0
15 console.log(r);
16
17 // Outputs: undefined
18 console.log(blue);
In the example above, there is a global variable named r. Or to put it another way, the global object
has a property named r. When the Color constructor is invoked without new, the constructor’s
this is set to the global object (in most cases). Which means that the constructor function has just
overwritten the global r variable with something that was intended to be a property of the blue
object.
Furthermore, because Color was invoked as an ordinary function, it didn’t automatically return a
new object, which means that blue is also undefined.
As you can imagine, debugging an issue like this can be time consuming and frustrating. So how do
we prevent these sorts of problems? Fortunately the answer is pretty straightforward.
1 // Global variable
2 r = "Rodent Of Unusual Size";
3
4 function Color(r, g, b) {
5 // Check whether `this` is a
6 // `Color` object.
7 if (this instanceof Color) {
8 this.r = r;
9 this.g = g;
10 this.b = b;
11 } else {
12 // If not, then we should invoke
13 // the constructor correctly.
14 return new Color(r, g, b);
15 }
16 }
17
18 // Dangerous invocation
19 // Means `this` is the global object
20 var blue = Color(0, 0, 255);
21
22 // Outputs: "Rodent Of Unusual Size"
23 console.log(r);
24
25 // Outputs: Color {r: 0, g: 0, b: 255}
26 console.log(blue);
Dealing with the Dangers of this in Constructors 11
In the updated Color constructor, the first thing we do is check whether this is an instance of Color.
It works because the new keyword will have already created the new object as an instance of the
constructor before the constructor function begins running.
If it isn’t a Color object, then we know the constructor was invoked incorrectly, so we skip all the
construction logic and have Color return the results of correctly invoking itself with new.
This means that the constructor is no longer in danger of clobbering the global object’s properties.
Of course, using this approach also means that developers may get into the bad habit of invoking
constructors without new. If you’d rather just force them to always use new, you could throw an
error instead, like so:
1 function Color(r, g, b) {
2 // Check whether `this` is a
3 // `Color` object.
4 if (this instanceof Color) {
5 this.r = r;
6 this.g = g;
7 this.b = b;
8 } else {
9 // If not, throw error.
10 throw new Error("`Color` invoked without `new`");
11 }
12 }
And that’s how you can make your custom constructors safely deal with a missing new keyword.
Using Dispatch Tables to Avoid
Conditionals in JavaScript
When writing code, one of the surest ways to keep things simple and straightforward is to avoid
conditionals when possible. Unfortunately, it is fairly common to see code with a lot of if, switch,
and case statements like the following:
1 function processUserInput(command) {
2 switch (command) {
3 case "north":
4 movePlayer("north");
5 break;
6 case "east":
7 movePlayer("east");
8 break;
9 case "south":
10 movePlayer("south");
11 break;
12 case "west":
13 movePlayer("west");
14 break;
15 case "look":
16 describeLocation();
17 break;
18 case "backpack":
19 showBackpack();
20 break;
21 }
22 }
Above we have a function for processing user input from a text adventure game. While it isn’t
terribly difficult to understand, it is more complicated than necessary. And as the number of
commands grow, the function can quickly become unwieldy. So what can we do to simplify it?
Using Dispatch Tables to Avoid Conditionals in JavaScript 13
1 var commandTable = {
2 north: function() { movePlayer("north"); },
3 east: function() { movePlayer("east"); },
4 south: function() { movePlayer("south"); },
5 west: function() { movePlayer("west"); },
6 look: describeLocation,
7 backpack: showBackpack
8 };
9
10 function processUserInput(command) {
11 commandTable[command]();
12 }
In this refactored version we are using a dispatch table¹³ to hold all the possible commands a user
can give, and the functions the program should call. That changes the processUserInput function
into a single line, and eliminates all conditionals.
If you are unfamiliar with bracket notation, it is just an alternate way of accessing a function’s
properties, with the advantage that you can use variables for the property’s name. For example, if
command is “north”, then commandTable[command] is equivalent to commandTable.north.
The fundamental change we made is transforming the conditionals into a data structure. And data
is much easier to manipulate than conditionals. In the future, if we want to add a new command, all
we have to do is add it to commandTable. No messing around with case or break statements required.
¹³https://en.wikipedia.org/wiki/Dispatch_table