Table of Contents
Foreword xi
Preface xiii
1 What Is Refactoring? 1
How Can You Guarantee Behavior Doesn't Change? 1
Why Don't We Care About Details of Implementation? 3
Why Don't We Care About Unspecified and Untested Behavior? 4
Why Don't We Care About Performance? 5
What Is the Point of Refactoring if Behavior Doesn't Change? 6
Balancing Quality and Getting Things Done 7
What Is Quality and How Does It Relate to Refactoring? 7
Refactoring as Exploration 9
What Is and Isn't Refactoring 9
Wrapping Up 10
2 Which JavaScript Are You Using? 11
Versions and Specifications 12
Platforms and Implementations 13
Precompiled Languages 15
Frameworks 16
Libraries 17
What JavaScript Do You Need? 18
What JavaScript Are We Using? 18
Wrapping Up 19
3 Testing 21
The Many Whys of Testing 24
The Many Ways of Testing 25
Manual Testing 26
Documented Manual Testing 26
Approval Tests 27
End-to-End Tests 29
Unit Tests 30
Nonfunctional Testing 32
Other Test Types of Interest 33
Tools and Processes 33
Processes for Quality 34
Tools for Quality 39
Wrapping Up 43
4 Testing in Action 45
New Code from Scratch 46
New Code from Scratch with TDD 54
Untested Code and Characterization Tests 72
Debugging and Regression Tests 77
Wrapping Up 86
5 Basic Refactoring Goals 87
Function Bulk 90
Inputs 93
Outputs 99
Side Effects 102
Context Part 1: The Implicit Input 103
This in Strict Mode 104
Context Part 2: Privacy 109
Is There Privacy in JavaScript? 121
Wrapping Up 122
6 Refactoring Simple Structures 125
The Code 127
Our Strategy for Confidence 130
Renaming Things 132
Useless Code 137
Dead Code 137
Speculative Code and Comments 137
Whitespace 139
Do-Nothing Code 139
Debugging/Logging Statements 143
Variables 143
Magic Numbers 143
Long Lines: Part 1 (Variables) 145
Inlining Function Calls 146
Introducing a Variable 147
Variable Hoisting 149
Strings 151
Concatenating, Magic, and Template Strings 152
Regex Basics for Handling Strings 153
Long Lines: Part 2 (Strings) 154
Working with Arrays: Loops, forEach, map 156
Long Lines: Part 3 (Arrays) 157
Which Loop to Choose? 158
Better Than Loops 160
Wrapping Up 161
7 Refactoring Functions and Objects 163
The Code (Improved) 163
Array and Object Alternatives 166
Array Alternative: Sets 166
Array Alternative: Objects 167
Object Alternative: Maps 169
Array Alternative: Bit Fields 173
Testing What We Have 174
Our Setup Test 176
Characterization Tests for classify 177
Testing the welcomeMessage 178
Testing for labelProbabilities 179
Extracting Functions 180
Getting Away from Procedural Code 180
Extracting and Naming Anonymous Functions 185
Function Calls and Function Literals 186
Streamlining the API with One Global Object 187
Extracting the classifier Object 191
Inlining the setup Function 192
Extracting the songList Object 192
Handling the Remaining Global Variables 193
Making Data Independent from the Program 195
Scoping Declarations: var, let, and const 195
Bringing classify into the classifier 196
Untangling Coupled Values 207
Objects with Duplicate Information 212
Bringing the Other Functions and Variables into classifier 214
Shorthand Syntax: Arrow, Object Function, and Object 220
Getting New Objects with Constructor Functions 226
Constructor Functions Versus Factory Functions 229
A class for Our Classifier 233
Choosing Our API 235
Time for a Little Privacy? 237
Adapting the Classifier to a New Problem Domain 239
Wrapping Up 242
8 Refactoring Within a Hierarchy 243
About "CRUD Apps" and Frameworks 243
Let's Build a Hierarchy 244
Lets Wreck Our Hierarchy 252
Constructor Functions 253
Object Literals 256
Factory Functions 258
Evaluating Your Options for Hierarchies 260
Inheritance and Architecture 261
Why Do Some People Hate Classes? 261
What About Multiple Inheritance? 262
Which Interfaces Do You Want? 265
Has-A Relationships 267
Inheritance Antipatterns 268
Hyperextension 269
Goat and Cabbage Raised by a Wolf 272
Wrapping Up 277
9 Refactoring to OOP Patterns 279
Template Method 280
A Functional Variant 283
Strategy 284
State 287
Null Object 293
Wrapper (Decorator and Adapter) 301
Facade 309
Wrapping Up 311
10 Asynchronous Refactoring 315
Why Async? 315
Fixing the Pyramid of Doom 318
Extracting Functions into a Containing Object 318
Testing Our Asynchronous Program 321
Additional Testing Considerations 323
Callbacks and Testing 326
Basic CPS and IoC 327
Callback Style Testing 328
Promises 331
The Basic Promise Interface 331
Creating and Using Promises 332
Testing Promises 335
Wrapping Up 336
11 Functional Refactoring 339
The Restrictions and Benefits of Functional Programming 340
Restrictions 340
Benefits 342
The Future (Maybe) of Functional Programming 345
The Basics 346
Avoiding Destructive Actions, Mutation, and Reassignment 346
Don't return null 354
Referential Transparency and Avoiding State 355
Handling Randomness 358
Keeping the Impure at Bay 358
Advanced Basics 361
Currying and Partial Application (with Ramda) 361
Function Composition 364
Types: The Bare Minimum 367
Burritos 371
Introducing Sanctuary 373
The null Object Pattern, Revisited! 375
Functional Refactoring with Maybe 380
Functional Refactoring with Either 383
Learning and Using Burritos 385
Moving from OOP to FP 387
Return of the Naive Bayes Classifier 387
Rewrites 391
Wrapping Up 392
12 Conclusion 395
A Further Reading and Resources 397
Index 401