BOOKS FOR PROFESSIONALS BY PROFESSIONALS® Daggett Expert JavaScript RELATED Expert JavaScript is your definitive guide to understanding how and why JavaScript behaves the way it does. Master the inner workings of JavaScript by learning in detail how modern applications are made. In covering lesser-understood aspects of this powerful language and truly understanding how it works, your JavaScript code and programming skills will improve. You will learn about core fundamentals of JavaScript, including deep dives into functions, scopes, closures, and practical object-oriented code. Mark Daggett explains clearly how closures, events, and asynchronous code really operate, as well as conventions and concepts to write JavaScript in a clear, pragmatic style. Many of the changes in ECMAScript6 and its implications are all explained. You’ll be introduced to modern workflow tools to make application development faster, more enjoyable, and ostensibly more profitable. You’ll understand how to measure code quality and write more testable JavaScript, and finally you’ll learn about real-world applications of JavaScript, including JavaScript- powered robots. JavaScript is one of the most powerful languages on the web today, and it is only getting stronger. This book will take you through the process of planning, coding, testing, profil- ing and finally releasing your application, at expert level. With more frameworks and more improvements than ever, now is the time to become an expert at JavaScript. Make this journey - use Expert JavaScript today. What you’ll Learn: • What is really going on underneath functions, in arguments, types, coercion, and scope • How closures, events, and asynchronous code work at a fundamental level • How to understand advanced topics including promise objects, coroutines, and generators • How to apply this newfound knowledge pragmatically to build the very best modern JavaScript applications Shelve in Web Development/JavaScript ISBN 978-1-4302-6097-4 User level: Advanced SOURCE CODE ONLINE 9 781430 260974 www.apress.com o D For your convenience Apress has placed some of the front matter material after the index. Please use the Bookmarks and Contents at a Glance links to access them. Contents at a Glance About the Author ���������������������������������������������������������������������������������������������������������������xiii About the Technical Reviewer ��������������������������������������������������������������������������������������������xv Acknowledgments ������������������������������������������������������������������������������������������������������������xvii Introduction �����������������������������������������������������������������������������������������������������������������������xix ■ Chapter 1: Objects and Prototyping �����������������������������������������������������������������������������������1 ■ Chapter 2: Functions �������������������������������������������������������������������������������������������������������31 ■ Chapter 3: Getting Closure �����������������������������������������������������������������������������������������������47 ■ Chapter 4: Jargon and Slang �������������������������������������������������������������������������������������������57 ■ Chapter 5: Living Asynchronously �����������������������������������������������������������������������������������79 ■ Chapter 6: JavaScript IRL ����������������������������������������������������������������������������������������������107 ■ Chapter 7: Style �������������������������������������������������������������������������������������������������������������131 ■ Chapter 8: Workflow ������������������������������������������������������������������������������������������������������151 ■ Chapter 9: Code Quality �������������������������������������������������������������������������������������������������175 ■ Chapter 10: Improving Testability ���������������������������������������������������������������������������������199 Index ���������������������������������������������������������������������������������������������������������������������������������219 v Introduction In my mind, good technical books are part mixtape, treasure map, and field journal. Expert JavaScript is the result of my efforts to successfully weave these forms together into a compelling and information-rich book about JavaScript. A mixtape, for those old enough to remember, is a curated collection of songs. These tapes were often made as gifts for friends, lovers, and those in between. The mixer would craft the tape by selecting personal favorites or organizing tracks along a conceptual thread. Often these tapes were a surrogate for the mixer, a way to be remembered by the listener when the tape was playing. This book is a mixtape for JavaScript that I made for you. These chapters cover some of my favorite aspects of the language, but also includes less-understood topics because they are not easily explained in a tweet or blog post. The long form format of a book affords these subjects the necessary room to breathe. As a child, I found the idea of finding a treasure map a thrilling prospect. I was captivated by the idea that anyone could become rich as long as they followed the map. This book will not lead you to buried treasure, but it is a map of sorts. I laid out these chapters to chart the inner workings of the language, which you can follow to the end. Dig through these concepts with me and you will unearth a deeper understanding of JavaScript than when you started. A field journal is kept by scientists. They are taught to keep a log of their thoughts, observations, and hunches about their subject. They may even tape leaves, petals, or other artifacts of nature between its pages. It’s a highly contextual diary about a subject of study filtered through a specific point of view. The purpose of the field journal is to be a wealth of information that the scientist can continually mine when they are no longer in the field. Expert JavaScript is my field journal of JavaScript, which I wrote to return to often. I will use it to help me remember and understand the particulars of the language. I encourage you to do the same. Scribble in the margins, highlight sections, and bookmark pages. It is not a precious object; it is meant to be a living document that is improved through your use. xix Chapter 1 Objects and Prototyping Practice does not make perfect. Only perfect practice makes perfect. —Vince Lombardi It may seem odd to include three chapters on core concepts of JavaScript in a book for experts. After all, these topics are some of the most rudimentary components of the language. My assertion is this: just as a person can speak a language without the ability to read or write it, so too can developers use the fundamental features of JavaScript and yet be blissfully unaware of their complexities. The goal of these chapters is to shine a light on some of the more shadowy portions of the language. These are the concepts that you may have always intended to learn or even assumed you already understood. Think of it as if you are descending into your brain’s basement, in which JavaScript is stored. Use this text like a flashlight to check for cracks in the foundation of your knowledge. This chapter and the next are meant to fill any fissures that might be revealed. Do not think of it as a needless review, but rather a structural assessment of your understanding of JavaScript. I will start with a high-level overview of the goals of the language. But before you know it, you will be flat on your belly, commando-crawling your way through the lesser-known concepts of JavaScript. I will describe in detail the important ideas related to objects and prototypes. Then, in the next chapters you’ll look at functions and closures, which are the building blocks of JavaScript. JavaScript from a Bird’s-Eye View What we call JavaScript is actually an implementation of the ECMAScript language specification. For JavaScript to be considered a valid version of ECMAScript, it must provide mechanisms to support the syntax and semantics defined in the spec. JavaScript as an implementation must provide the programmer affordances to use the various types, properties, values, functions, and reserved words that make up ECMAScript. Once a version of JavaScript conforms to ECMAScript, language designers are free to embellish their version with extra features and methods as they see fit. The ECMAScript specification explicitly allows this kind of flourish, as you can read here: A conforming implementation of ECMAScript is permitted to provide additional types, values, objects, properties, and functions beyond those described in this specification. In particular, a conforming implementation of ECMAScript is permitted to provide properties not described in this specification, and values for those properties, for objects that are described in this specification. A conforming implementation of ECMAScript is permitted to support program and regular expression syntax not described in this specification. 1 Chapter 1 ■ ObjeCts and prOtOtyping The fact that these extra features can exist in parallel with the core elements and still be considered a valid implementation is a sign of how progressive the ECMAScript standards body is. The looseness of what qualifies as ECMAScript is simultaneously a benefit and a drawback. Although the flexibility to add new features encourages language designers to innovate, it can leave developers in a bad spot trying to write clever polyfills1 to support the differences between the various implementations and runtime environments. The ECMAScript specifications change over time, and occur for a variety of reasons (too many to enumerate here). Primarily, though, these changes are an attempt to codify new approaches to old problems or to support advancements in the larger computing ecosystem. The changing specification represents an attempt to formalize the evolutionary processes within the language. Therefore, although I’m talking about “core concepts” as if they are immutable, in reality they are not. The concepts explored in this chapter are foundational and important, but my advice to the reader is to stay on your toes. Scripting by Design As its name implies, ECMAScript is a scripting language used to interact with a host environment programmatically. A host system, be it a browser, a server, or piece of hardware, exposes control points for JavaScript to manipulate. Most host environments allow JavaScript to trigger only aspects of the system that are already under the user’s control (albeit manually). For example, where a user of a browser might click a link on a web page using a mouse or finger, JavaScript could trigger the same event programmatically: document.getElementById('search').click(); Traditionally, ECMAScript was almost exclusively intended as a tool for web scripting within browsers. Developers employed it to enhance the user’s experience when browsing a web page. Today, ECMAScript is equally at home on the server as it is in the browser, thanks to stand-alone engines such as V8 or TraceMonkey. The ECMAScript standards body foresaw this growing divergence between how developers have traditionally used JavaScript, and where much of the recent growth has been. Wisely when defining what “web scripting” is in the most recent specification, it provided two examples that present the various contexts in which ECMAScript is popular today: A web browser provides an ECMAScript host environment for client-side computation including, for instance, objects that represent windows, menus, pop-ups, dialog boxes, text areas, anchors, frames, history, cookies, and input/output. Further, the host environment provides a means to attach scripting code to events such as change of focus, page and image loading, unloading, error and abort, selection, form submission, and mouse actions. Scripting code appears within the HTML and the displayed page is a combination of user interface elements and fixed and computed text and images. The scripting code is reactive to user interaction and there is no need for a main program. A web server provides a different host environment for server-side computation including objects representing requests, clients, and files; and mechanisms to lock and share data. By using browser- side and server-side scripting together, it is possible to distribute computation between the client and server while providing a customized user interface for a Web-based application. Each Web browser and server that supports ECMAScript supplies its own host environment, completing the ECMAScript execution environment. 1http://remysharp.com/2010/10/08/what-is-a-polyfill/ 2 Chapter 1 ■ ObjeCts and prOtOtyping ■ Note at the time of this writing, the arrival of the newest version of eCMascript 6 (named “harmony”) was imminent, and although not officially released, many of the proposed changes are already being supported by runtime engines and browsers. this chapter is an exhaustive look at the core of the language, which also includes some of the new features introduced in harmony. i will take special care to alert the reader when i am explaining a proposed feature that may have limited support. Objects Overview JavaScript is an object-oriented programming (OOP) language created by Brendan Eich, which he released after a few weeks of development while working for Netscape. Although JavaScript has “Java” in the name, it has little to do with the Java language. In an interview with InfoWorld, Eich explained the turn of events that lead to the language being renamed “JavaScript:” InfoWorld: As I understand it, JavaScript started out as Mocha, then became LiveScript and then became JavaScript when Netscape and Sun got together. But it actually has nothing to do with Java or not much to do with it, correct? Eich: That’s right. It was all within six months from May till December (1995) that it was Mocha and then LiveScript. And then in early December, Netscape and Sun did a license agreement and it became JavaScript. And the idea was to make it a complementary scripting language to go with Java, with the compiled language.2 Even a casual comparison of the two languages reveals glaring differences. Unlike Java, JavaScript is not complied, does not enforce strict typing, or have a formal class–based inheritance mechanism. Instead, JavaScript is executed in the context of a host environment (e.g., a web browser), supports dynamic typing of variables, and implements inheritance through a prototype chain instead of classes. Therefore, we should probably chalk up the similarities between the names as the desire for a marketing synergy instead of an attempt to create a meaningful linkage between the two languages. Yet for all their differences, both Java and JavaScript are members of the OOP family. Being object oriented means objects control a program’s operation by communicating with each other. OOP languages are some of several popular programming paradigms that include, among others, Functional, Imperative, and Declarative. ■ Note just because javascript is conceived as an object-oriented language does not mean that it is restricted to that paradigm. For example, the popular library Underscore.js3 is written in the Functional programming style. Objectified What does it mean to be an OOP language? This may seem like an unnecessary question to ask experienced programmers, but the act of answering this question gives you the space needed to evaluate JavaScript’s approach to OOP. You will spend the bulk of this book designing and thinking in terms of objects and their interrelationships, but it is important to remember that objects are just one of many possible metaphors used to model programs. 2http://www.infoworld.com/d/developer-world/javascript-creator-ponders-past-future-704 3http://underscorejs.org/ 3 Chapter 1 ■ ObjeCts and prOtOtyping Metaphors are seductive and often obscure as much as they reveal; their affordances may allow you to cleanly conceive a solution for one problem while needlessly complicating another. As you answer what it means to be OOP, reflect on your own understandings and presuppositions. You may find that you’ve biased your own outlook on the concept. Objects in JavaScript are little more than containers for properties. I’ve heard programmers describe them as “property bags,” which evokes a pleasing visual. Every object can have zero or more properties, which can either hold a primitive value or pointer that references a complex object. JavaScript can create objects in three ways: using literal notation, the new() operator, or the create() function. In their simplest form, these three approaches can be expressed like this: var foo = {}, bar = new Object(), baz = Object.create(null); The difference between these approaches is how the object is initialized, which we’ll sift through later. For now, I will describe the ways to embellish objects by assigning them custom properties. Property Manager Many developers assume that an object’s property is only a container that can be assigned a name and a value. In actuality though, JavaScript gives the developer a series of powerful property descriptors that further shape how the property behaves. Let’s iterate over them now: configurable When this attribute is set to true, the affected property can be deleted from the parent object, and the property’s descriptor can be modified later. When set to false, the property’s descriptor is sealed from further modifications. Here is a simple example: var car = {}; // A car can have any number of doors Object.defineProperty(car, 'doors', { configurable: true, value: 4 }); // A car must have only four wheels Object.defineProperty(car, 'wheels', { configurable: false, value: 4 }); delete car.doors; // => "undefined" console.log(car.doors); delete car.wheels; // => "4" console.log(car.wheels); 4 Chapter 1 ■ ObjeCts and prOtOtyping Object.defineProperty(car, 'doors', { configurable: true, value: 5 }); // => "5" console.log(car.doors); // => Uncaught TypeError: Cannot redefine property: wheels Object.defineProperty(car, 'wheels', { configurable: true, value: 4 }); As you can see in the previous example, wheels becomes fixed while doors remains malleable. A programmer might want to revoke the configurable attribute of a property as a form of defensive programming to prevent an object from being modified much like built-in objects of the language do. enumerable Enumerable properties appear if an object’s properties are iterated over using code. When set to false, those properties cannot be iterated over. Here is an example: var car = {}; Object.defineProperty(car, 'doors', { writable: true, configurable: true, enumerable: true, value: 4 }); Object.defineProperty(car, 'wheels', { writable: true, configurable: true, enumerable: true, value: 4 }); Object.defineProperty(car, 'secretTrackingDeviceEnabled', { enumerable: false, value: true }); // => doors // => wheels for (var x in car) { console.log(x); } 5 Chapter 1 ■ ObjeCts and prOtOtyping // => ["doors", "wheels"] console.log(Object.keys(car)); // => ["doors", "wheels", "secretTrackingDeviceEnabled"] console.log(Object.getOwnPropertyNames(car)); // => false console.log(car.propertyIsEnumerable('secretTrackingDeviceEnabled')); // => true console.log(car.secretTrackingDeviceEnabled); As you can see from the previous example, even though a property is not enumerable it does not mean the property is hidden altogether. The enumerable attribute can be used to dissuade a programmer from using the property, but should not be used as a method to secure an object’s properties from inspection. writable When true, the value associated with the property can be changed; otherwise, the value remains constant. var car = {}; Object.defineProperty(car, 'wheels', { value: 4, writable: false }); // => 4 console.log(car.wheels); car.wheels = 5; // => 4 console.log(car.wheels); Inspecting Objects In the last section, you learned how to define your own properties on objects you create. Just as in life, it’s helpful to know how to read and write, so in this section you’ll learn how to dig through the underbrush of objects in JavaScript. What follows is a list of functions and properties worth knowing when it comes to inspecting objects. Object.getOwnPropertyDescriptor In the last section, you saw the various ways to set the attributes of a property. Object.getOwnPropertyDescriptor gives you a detailed description of those settings for any property of an object: var o = {foo : 'bar'}; // Object {value: "bar", writable: true, enumerable: true, configurable: true} Object.getOwnPropertyDescriptor(o,'foo'); 6
Description: