setTimeout
), the arrival of data from a website (in the case of XMLHttpRequest.send
), or the click of a mouse, among many possibilities. JavaScript has an event loop that consumes the functions thus enqueued one at a time.
From a design point of view, this makes your life easier than it would be in a true multi-threaded environment. You never have to worry about getting interrupted, or about other objects accessing your variables when you think you have control.
It also means you shouldn’t hog the processor!
In Chapter 6, you will see how JavaScript Promise
s let you write code that does not block, yet is not a confusing scatter of event-handlers awkwardly connected by variables.
Avoiding JavaScript’s Pitfalls in Larger Systems
Why is a system that contains 50 classes (or objects, in JavaScript) more than ten times as challenging to write and maintain as a system that contains five? With five objects, even if each one draws on the services of all the others, there are at most 20 channels of communication (each of 5 objects calling 4 others – allowing ourselves to count A calling B as well as B calling A). With 50, there are 2450 (50 times 49) – more than 100 times as many.
With the advent of Single-Page Applications, node.js
, and other ways of making JavaScript shoulder the burdens of larger and larger systems on both client and server, the best JavaScript developers get serious about trimming those channels of communication to a bare minimum.
Where an object must interface with others to do its job, the connections are managed assiduously to ensure that they function properly in all circumstances.
This section will suggest ways to meet these goals.
Scripts Are Not Modules
Just last week, we were on the website of a company that makes a certain specialized device for user input. They had helpfully provided sample JavaScript code for using their device.
Argh! Their JavaScript library, suggested for all programmers to use, was over 1900 lines of one global variable or function after another – over 200 global functions in all. Most of the global functions were at least named so that collisions with other libraries were unlikely, but some, such as makeUri
or toHex
, were not.
This is the “scripting” heritage of JavaScript at work. In the old days, when your script was probably the only one on the page, there was little harm in adding to the global namespace. With today’s JavaScript applications, that is never the case.
Your script is in no way isolated because it is in its own .js
file. If your file starts with
as this one did (the names have been changed to protect the guilty), then myVariable
is visible to all the other scripts in your application, and the makeValue
function evidently is, too.
JavaScript presents an unusually diverse menu of choices for creating modules that properly encapsulate their data and functions. Script files are not one of them! (You will read more about data encapsulation in Chapter 3.)
Nested Functions Control Scope
In C# or Java, one class can contain another. However, this practice is not widespread. Microsoft even cautions against it. Code Analysis warning 1034 is “Nested types should not be visible” and their rationale is “Nested types include the notion of member accessibility, which some programmers do not understand clearly” (https://msdn.microsoft.com/en-us/library/ms182162.aspx).
JavaScript does not have classes, but nested functions serve the same purpose of organizing the code hierarchically. Crucially, the hierarchy not only helps the programmer find what he’s looking for; it also helps the program minimize the scope of its variables and functions. That’s key to keeping a large system under control, and it is the warp and woof of the best JavaScript code.
Recall this snippet from Listing 1-1:
The inner line
function has a member function, line.x
. Although x
is a member of line
, it cannot see line
’s local variables, such as segments. Both line
and line.x
can see the getX
variable in the enclosing function. Combine this sort of artfulness with closures, and you have some very powerful tools for keeping large JavaScript systems under control.
Coding by Contract
There is no better way to make a large system more manageable than to make it smaller. JavaScript, with the extraordinary flexibility of pervasive duck-typing, lets you write a little code that can do a lot. (Recall the variety of inputs handled in the D3 case study earlier in the chapter.)
The flip side is that you never know what someone is going to throw at your software.
If your function expects its arguments to meet certain requirements, consider validating them. In Chapters 16 through 21, you will see one way to do this as unobtrusively as possible: the ContractRegistry
.
In a nutshell, the registry allows you to verify anything you wish about an argument or return value, without adding any code to your function. It does this through the magic of aspect-oriented programming (covered in Chapter 2) and in such a way that the overhead of validation can be eliminated in the shipped version.
Applying the Principles of Software Engineering
Have you ever been to a concert by a virtuoso musician? Maybe you play the same instrument, and you’ve marveled that the performer makes it look so easy. The truth is, he makes it look easy because it is easy – for him. And the reason it’s easy for him is that he has trained his fingers to move efficiently, trained his body to relax and breathe, trained his mind to listen to the music rather than be distracted by anxiety.
He probably learned the piece by playing it very, very slowly at first. Only when he had mastered it completely at that pace did he take the metronome up one notch. Thus, he did not practice-in any mistakes. One of us, a classical guitarist, went to a masterclass taught by one of the world’s best. The teacher boasted, “I bet I can play this piece slower than any of you.” He has learned that the quickest way to learn to play a piece flawlessly is to play it slowly.
When you have mastered the principles in this section, you will write flawless software more quickly and with less effort. Your fellow developers will look at your code and say, “He makes it look so easy!”
The SOLID Principles
The acronym SOLID was coined by Michael Feathers as a way to remember the five principles of object-oriented design that Robert Martin set forth in the late 1990s (summarized at http://www .objectmentor.com/resources/articles/Principles_and_Patterns.pdf). They are:
● The Single Responsibility Principle
● The Open/Closed Principle
● The Liskov Substitution Principle
● The Interface Segregation Principle
● The Dependency Inversion Principle
The Single Responsibility Principle
Stated in its most extreme form, the Single Responsibility Principle is that a class (or function, in JavaScript) should have only one reason to change.
That is a very tall order. Surely every line of code represents something that could change. Must every function consist of just one line of code?
No, but don’t give up on this principle too quickly. Consider once more the rj3.svg.line
function in Listing 1-1.