Page
1
(This page has no text content)
Page
2
MEAP Edition Manning Early Access Program Secrets of the JavaScript Ninja, Third Edition Version 10 Copyright 2026 Manning Publications For more information on this and other Manning titles go to manning.com. © Manning Publications Co. To comment go to liveBook Licensed to THIAGO BANDEIRA <thiago@lar.ifce.edu.br>
Page
3
welcome Thank you for purchasing the MEAP for Secrets of the JavaScript Ninja, Third Edition. The world of JavaScript has changed a great deal in the years since the second edition Secrets of the JavaScript Ninja was released. Back in those days, ES6 (later renamed ES2015) was cutting-edge, a breath of fresh air for a language that had stagnated for years. React, ESLint, and TypeScript were all in their infancy. Most websites still used jQuery, the groundbreaking framework created by John Resig, author of the first edition. What I’ve always loved about Secrets of the JavaScript Ninja is the way it explains concepts from the ground up in a way that’s accessible to beginners and clarifying for experienced developers. I’ve set out to write a new edition that retains that spirit while covering topics that are relevant to today’s engineers. As with previous editions, the book’s focus is on developing a deep understanding of JavaScript language features and concepts. You’ll learn about the distinction between function declarations and function expressions, the connection between classes and prototypes, and how “async” functions work in a single-threaded language. Along the way, you’ll be introduced to relevant tools that can help you catch bugs early and keep your code tidy. It’s my privilege to work on this legendary book. With your feedback, I hope this new edition can be as impactful as its predecessors. Please feel free to share your questions and comments in the liveBook Discussion forum. —Trevor Burnham © Manning Publications Co. To comment go to liveBook Licensed to THIAGO BANDEIRA <thiago@lar.ifce.edu.br>
Page
4
brief contents CHAPTERS 1 JavaScript is everywhere 2 Using modern JavaScript tools 3 Working with Node.js and npm 4 Building web applications 5 First-class functions for the novice: denitions and arguments 6 Functions for the journeyman: understanding function invocation 7 Functions for the master: closures and scopes 8 Functions for the future: promises and generators 9 Objects, prototypes, and classes 10 Dealing with collections 11 Buers and streams 12 Wrangling regular expressions 13 Modules and build systems 14 Going multi-threaded with Workers 15 Operating on JavaScript code Appendix A. Setting up Node.js and npm © Manning Publications Co. To comment go to liveBook Licensed to THIAGO BANDEIRA <thiago@lar.ifce.edu.br>
Page
5
1 JavaScript is everywhere Atwood’s Law: any application that can be written in JavaScript, will eventually be written in JavaScript. —Jeff Atwood, co-founder of Stack Overflow This chapter covers What makes JavaScript special How JavaScript evolves over time Three best practices in JavaScript development JavaScript began as a 10-day project to add a scripting language to Netscape’s web browser back in 1995. Today, it’s one of the most widely used programming languages in the world. Look around and you’ll see it everywhere: websites, servers, mobile and desktop apps, game consoles, IoT devices, and even your car. It’s truly remarkable for a single language to be so useful across so many domains. How did it make the leap from the web browser to the wider world of computing? In a word: evolution. For years, JavaScript had a reputation for being painfully slow and full of strange quirks, with none of the niceties that developers enjoyed with other languages: rich IDE integration, static analysis and debugging tools, a package distribution system, and so on. As the only language that could run in web browsers, developers grudgingly tolerated it, but few of them loved it. But gradually, that changed. Competition between browser vendors led to faster JavaScript engines, and cooperation between them led to the ECMAScript standards process for new language features (see Section 1.1.) The emergence of the Node.js runtime led to an explosion of JavaScript applications outside of the browser, along with a package distribution system and a rich set of tools for JavaScript written in JavaScript (see Section 1.2). 1 © Manning Publications Co. To comment go to liveBook Licensed to THIAGO BANDEIRA <thiago@lar.ifce.edu.br>
Page
6
Thanks to that evolution, JavaScript is both enormously versatile and—for most purposes— reasonably performant. And the developer experience has improved dramatically. The kind of rich IDE integration and static analysis that JavaScript developers could only dream of are now available, thanks to TypeScript. TypeScript has become so popular that it’s become synonymous with JavaScript development for many developers. We even considered naming this book Secrets of the TypeScript Ninja! But ultimately, TypeScript is just a tool for developing JavaScript. This book integrates TypeScript in that spirit. We’ll briefly introduce TypeScript in Section 1.3 and show you how to use it in Chapter 2. This book is designed to give you a thorough understanding of JavaScript fundamentals. Armed with that knowledge, you’ll be able to create robust and performant applications in any domain you choose. Our approach is to introduce language concepts from the ground up, covering the basics before working our way up to “secret” techniques that distinguish the JavaScript Ninja from the ordinary developer. We hope you enjoy your ninja training. Ganbatte! 1.1 Understanding the JavaScript language People often feel that if they know C# or Java, they already have a pretty solid understanding of how JavaScript works. But it’s a trap! When compared to other mainstream languages, JavaScript is much more functionally oriented. Some JavaScript concepts differ fundamentally from those of most other languages. These differences include the following: Functions are first-class objects—In JavaScript, functions coexist with, and can be treated like, any other JavaScript object. They can be assigned to variables, passed around as function arguments, and even created by other functions. We devote much of Chapter 5 to exploring some of the wonderful benefits that functions as first-class objects bring to our JavaScript code. Prototype-based object orientation—Unlike other mainstream programming languages (such as C#, Java, and Ruby), which use class-based object orientation, JavaScript uses prototypes. Modern JavaScript does offer a class keyword, but under the hood, those classes are defined in terms of prototypes. We’ll go deep into prototypes and how they relate to classes in Chapter 9. Single-threaded—JavaScript code runs in a single thread. To perform asynchronous operations, JavaScript code uses callbacks and promises. We’ll take a brief look at these patterns in Chapter 3 and go into more depth in Chapter 8. Then in Chapter 17 we’ll look at workers, a feature that allows you to delegate work outside of the main thread. JavaScript consists of a close relationship between objects and prototypes, and functions and closures. Understanding the strong relationships between these concepts can vastly improve your JavaScript programming ability, giving you a strong foundation for any type of application development, regardless of whether your JavaScript code will be executed in a web browser, on a server, or in an app. 2 © Manning Publications Co. To comment go to liveBook Licensed to THIAGO BANDEIRA <thiago@lar.ifce.edu.br>
Page
7
In addition to these fundamental concepts, other JavaScript features can help you write more elegant and more efficient code. Some of these are features that seasoned developers will recognize from other languages, such as Java and C#. A few of the language features we’ll be covering are: Promises, which give us better control over asynchronous code Advanced array methods, which make array-handling code much more elegant Maps, which we can use to create dictionary collections; and sets, which allow us to deal with collections of unique items Regular expressions, which let us simplify what would otherwise be complicated pieces of code Modules, which we can use to break code into smaller, relatively self-contained pieces that make projects more manageable Having a deep understanding of the fundamentals and learning how to use advanced language features to their best advantage can elevate your code to higher levels. Honing your skills to tie these concepts and features together will give you a level of understanding that puts the creation of any type of JavaScript application within your reach. 1.1.1 JavaScript runtimes and engines JavaScript’s popularity has taken it to all kinds of places: Web browsers, servers, mobile and desktop apps, game consoles, IoT devices—anywhere you look, you’re more than likely to find JavaScript code. That wide range of applications has led to a variety of runtimes, environments that can execute JavaScript code. Node.js is the most well-known non-browser runtime, though it faces increasing competition from the likes of Deno and Bun. Under the hood, every runtime uses an engine to convert JavaScript code to machine instructions that the CPU can run. Modern JavaScript engines use just-in-time compilation to optimize code as it runs, and those engines are constantly being improved to achieve higher performance and support new ECMAScript features. Two notable engines are V8, which powers Google Chrome and Node.js; and JavaScriptCore, which powers Safari and Bun. In general, you shouldn’t have to worry about which engine will be executing your code. However, for the sake of writing efficient JavaScript, it’s helpful to have some knowledge of how your code will translate to machine code. Throughout the book, we’ll provide tips for writing code that performs well in modern JavaScript engines. 3 © Manning Publications Co. To comment go to liveBook Licensed to THIAGO BANDEIRA <thiago@lar.ifce.edu.br>
Page
8
Figure 1.1 How a JavaScript runtime brings JavaScript code to life. The JavaScript runtime also provides a set of API bindings. These APIs are what allow your code to interact with its host environment. In the browser, you might use APIs to fetch data over the network, listen for user events, and update the DOM. In Node.js, you might use APIs to listen to a TCP socket, log data to a disk, and spawn subprocesses. Some JavaScript code is written to run across a wide range of runtimes. For example, Lodash (https://lodash. com) is a popular library that provides a variety of low-level utility functions and is widely used in browsers, servers, and apps. Other JavaScript code is written with a specific runtime in mind. However, regardless of the author’s intent, code can cross over. A runtime like Node.js can simulate a browser-like environment with a library like jsdom (https://github. com/jsdom/ jsdom). Simulating a browser can be useful for testing, or for pre-rendering HTML. Conversely, it’s possible to simulate Node.js inside a browser with WebContainers (https://webcontainers. io). Simulating Node.js can be useful for running code in a secure sandbox, or for spinning up a development environment without having to install anything. NOTE Most of the techniques we’ll be discussing in this book will work in any modern JavaScript runtime, whether it’s a browser or a standalone runtime like Node.js. We’ll note any exceptions to this rule as we go. This book will focus on Node.js in Chapter 3, and on browsers in Chapter 4. Even if you only write code for browsers, you’ll likely want to use Node.js (or one of the competing JavaScript runtimes) for your development environment and build pipeline. 4 © Manning Publications Co. To comment go to liveBook Licensed to THIAGO BANDEIRA <thiago@lar.ifce.edu.br>
Page
9
1.1.2 How does JavaScript get new features? Shortly after it was introduced by Netscape in 1995, a committee was formed to standardize the language. Due to complications around Sun Microsystems’ trademark on the word “Java,” which Netscape was using under license, that committee is now known as the ECMAScript Committee. For all practical purposes, ECMAScript and JavaScript are synonyms: ECMAScript is used in the context of official specifications, while JavaScript remains the popular term everywhere else. For years, the ECMAScript standard saw relatively few changes, focusing on ensuring consistent behavior across different implementations. That all changed in 2015, when the ES6 standard (later renamed ES2015) brought a slew of exciting new features: arrow functions, maps, promises, modules, and many more. Since then, every year has brought a new ECMAScript standard: ES2016, ES2017, etc. This steady drip of new JavaScript features is exciting, but it also means that you need to be mindful of which features are supported by the runtimes that will be executing your code— especially when writing code for web pages, where the runtime is the user’s possibly-ancient browser. We’ll discuss some strategies for targeting a wide range of browsers in Chapter 14. If you want to see which runtimes support a particular feature, check the ECMAScript compatibility table at https://compat- table.github. io/compat- table. 1.1.3 Transpilers and polyfills give us access to tomorrow’s JavaScript today If you want to take advantage of the newest JavaScript features without waiting for them to be supported by every runtime your code might run in, you’ve got options: One option is to use a transpiler (“transformation + compiler”), a tool that takes cutting-edge JavaScript code and transforms it into functionally equivalent code that limits itself to features from older ECMAScript standards. The concept of transpiling JavaScript was pioneered by Babel (https://babeljs. io). We’ll discuss Babel and other build tools in Chapter 14. Also, if you use TypeScript, the TypeScript compiler will automatically transpile syntactic features to target the ECMAScript version of your choice. We’ll discuss TypeScript’s other benefits in section 1.3.1, and we’ll show you how to use it in Chapter 4. Another option is to use a polyfill, which is code that defines a feature in runtimes where it isn’t natively provided. For example, if you want to write promises (which were added in ES6) but need to support Internet Explorer 11 (which is stuck on ES5), you can use a polyfill that defines a global Promise function with the same functionality as the ES6 API. Polyfills are often used in conjunction with transpilation; in fact, Babel can be configured to automatically inject the necessary polyfills as it transpiles your code. TypeScript doesn’t do this, which is one reason why TypeScript and Babel are often used together. Unfortunately, some features just can’t be efficiently implemented in older runtimes. An example is Regex lookbehind assertions, which were added in ES2018. In runtimes that don’t have that feature, Regexes that use lookbehind assertions will simply fail, whether the code is transpiled or not! To avoid such issues, it’s important to remain aware of which runtimes you expect your code to run in and which features they support. Better yet, you can set up automated tests for those runtimes. We’ll discuss some approaches to testing JavaScript later in this chapter. 5 © Manning Publications Co. To comment go to liveBook Licensed to THIAGO BANDEIRA <thiago@lar.ifce.edu.br>
Page
10
1.2 Navigating the world of frameworks One of JavaScript’s strengths is its rich open-source ecosystem, which puts millions of libraries at your fingertips. Figuring out how to select the right libraries for your project is a critical skill for any JavaScript developer. And for most projects, the first question you’ll want to answer is: Which framework should I use? A framework is a library that powers the core functionality of your application. If you’re writing a web application, you’ll use your framework to render HTML. If you’re writing a server application, you’ll use your framework to parse HTTP requests and send responses. You could achieve the same functionality without a framework, but using a framework that fits your project can save you an enormous amount of time. Nearly all modern JavaScript applications are built on a framework. Throughout this book, we’ll reference popular frameworks that can help implement the features being discussed. This section will provide a quick overview of the JavaScript framework landscape. 1.2.1 Web UI frameworks In the early days of JavaScript, its main use was to add a dash of interactivity to otherwise static websites. The browser would download and render a complete HTML document, then run a script to add enhancements like form validation. The predominant JavaScript frameworks (most notably jQuery) were thin wrappers around browser APIs, aimed at smoothing over cross-browser differences and making common operations a bit easier. Writing complex web applications in those days was awkward, since it required going back and forth between HTML and JavaScript. Then in the 2010s, a new generation of JavaScript frameworks arose that offered the ability to write HTML within JavaScript code. One of those frameworks, React (https://react. dev), soon ruled the web. React introduced the JSX language, which allows JavaScript and HTML to be written together in a way that feels natural: 6 © Manning Publications Co. To comment go to liveBook Licensed to THIAGO BANDEIRA <thiago@lar.ifce.edu.br>
Page
11
Under the hood, React uses a virtual DOM: It constructs a tree representing what the HTML on the page should look like, compares that tree to the existing HTML on the page, and then updates the page to make the two trees match. It’s a clever technique that saves developers the trouble of interacting with the DOM directly. We’ll take a closer look at JSX in Chapter 3. React has no shortage of challengers to the throne, including Angular (https://angular. io), Vue (https://vuejs. org), and Svelte (https://svelte. dev). Many of the alternatives use JSX or a similar templating language, putting their own spin on the React paradigm rather than overturning it. A key consideration in picking among these frameworks is the open-source ecosystem: A newer framework may be more elegant and efficient than React, but using React allows you to tap into thousands of readymade component libraries. NOTE The world of JavaScript libraries moves fast! One excellent resource for keeping up with the state of the art in browser-based JS is Chen Cheng’s Awesome JavaScript repo: https://github. com/sorrycc/awesome-javascript import React, {useState} from "react"; function Counter() { const [count, setCount] = useState(0); return ( <div> <div>The count is {count}.</div> <button onClick={() => { setCount(count + 1); > Increment count </button> </div> } 7 © Manning Publications Co. To comment go to liveBook Licensed to THIAGO BANDEIRA <thiago@lar.ifce.edu.br>
Page
12
The rise of powerful web UI frameworks has made it much easier to develop rich web applications, but that convenience has come at a cost: Instead of serving a complete HTML document to the browser, many websites now serve a blank page with a large amount of JavaScript code. The user doesn’t get to see any content until that code has been downloaded and executed. As a result, loading those sites has gotten a lot slower. Fortunately, there’s a solution: server-side rendering (SSR). With SSR, the same code that would render the page in the browser is used to render the page on the server, allowing it to send the browser a complete HTML document before sending the JavaScript code. Because SSR requires the same code to be runnable on both the server and the browser, it can be a challenge to implement. Fortunately, some server frameworks are designed to help meet that challenge. 1.2.2 Server frameworks Thanks to Node.js, JavaScript has become one of the most popular languages for server development, particularly web servers. Since Node.js only provides low-level primitives for dealing with HTTP requests, nearly all of those servers use a framework. At its simplest, a server framework routes HTTP requests to handler functions that implement the logic needed to generate a response. Express (https://expressjs. com) is a popular choice for developers seeking a minimal foundation for their server: For developers looking for more out-of-the-box functionality, Adonis (https://adonisjs. com) comes equipped with support for authentication, internationalization, an ORM for querying SQL databases, and more. For a content-centric site, like a blog or online store, a CMS framework like Keystone (https://keystonejs. com) can provide a full-featured foundation with minimal configuration. Perhaps most interesting are the full-stack frameworks that blur the boundaries between the web server and the UI. Next.js (https://nextjs. org), Remix (https://remix. run), and Astro (https://astro. build) are designed to run high-performance React websites: They perform server-side rendering by default, and they split up the site’s JavaScript code into chunks to be delivered only on the pages where they’re needed. Additionally, these frameworks provide a first-class development experience for frontend engineers, with a local dev server that incorporates Hot Module Replacement (HMR) to push changes to the browser without a refresh. #A Create an Express server #B Define a handler for GET requests with the path “/ping” #C Send a response with the message body “OK” import express from "express"; const app = express(); #A app.get("/ping", (req, res) => { #B res.send("OK"); #C 8 © Manning Publications Co. To comment go to liveBook Licensed to THIAGO BANDEIRA <thiago@lar.ifce.edu.br>
Page
13
NOTE You can find a curated list of the latest server frameworks, along with all kinds of other useful Node.js libraries, at Sindre Sorhus’ Awesome Node.js repo: https://github. com/sindresorhus/ awesome-nodejs We’ll look at using JavaScript as a server language in Chapter 2. 1.2.3 App frameworks JavaScript’s popularity has expanded beyond the web into the realm of desktop and mobile applications. A major advantage of writing apps in JavaScript is that you can “write once, run anywhere”: Instead of writing a Swift application for iOS and macOS, a Kotlin application for Android, and a C# Application for Windows, you can write a single application in JavaScript and deploy it to all of those platforms—and the web, too! At its simplest, an app framework can be a thin native wrapper around an embedded web view. That allows web developers to leverage their skillset, since the UI for the app is ordinary HTML and CSS. Popular frameworks that use a web view include Electron (https://www. electronjs. org) for desktop apps and Ionic (https://ionicframework. com) for mobile apps. Apps that use an embedded web view have earned a negative reputation for being low-quality and disregarding platform-specific design standards, but that doesn’t have to be the case. With the right component libraries and some attention to detail, apps that use a web view can look great on any system. Some popular, well-designed apps that use a web view include Slack, Notion, and Figma. React Native (https://reactnative. dev) takes a different approach: Instead of rendering HTML and CSS in a web view, it renders UI components specific to the target platform. Just as with React for the web, you write your code in JSX; but instead of using HTML elements like <div>, you use React Native-specific constructs like <View>. which gets translated to UIView on iOS and android.view on Android. The result is an app that looks and feels just like it was written separately for each platform. Some popular apps that use React Native include Discord, Pinterest, and Skype. JavaScript has been blessed with a passionate open-source community that tirelessly maintains a wide range of frameworks. With the right framework and the skills of a JavaScript ninja, you can build practically any project you can imagine. 1.3 Using current best practices Mastery of the JavaScript language and a grasp of ECMAScript standards are important parts of becoming an expert web application developer, but they’re not the complete picture. To enter the big leagues, you also need to exhibit the traits that scores of previous developers have proven are beneficial to the development of quality code. These traits are known as best practices, and in addition to mastery of the language, they include such elements as Type checking Linting/formatting Testing 9 © Manning Publications Co. To comment go to liveBook Licensed to THIAGO BANDEIRA <thiago@lar.ifce.edu.br>
Page
14
It’s vitally important to adhere to these practices when coding, and we’ll use them throughout the book. Let’s examine some of them next. 1.3.1 Type checking One of JavaScript’s most notable quirks is its flexible type system: Rather than throwing errors when operations are performed across incompatible types, the language bends over backward to make those operations happen. The results are counterintuitive, to say the least. For example, [] + {} produces the string "[object Object]", while [] + "" yields the string ""! To be a JavaScript Ninja, you don’t need to know every JavaScript type coercion rule by heart; you just need to know how to avoid them! Unexpected types are one of the most common sources of errors in JavaScript code, as anyone who’s seen the word undefined appear in the middle of a web page can attest. To solve this problem, Microsoft created and open-sourced a tool called TypeScript (https://www. typescriptlang. org). TypeScript is a language that adds type annotations to JavaScript. The TypeScript compiler looks through the code to make sure that the types satisfy the rules. For example, passing a string to a function that expects a number would result in an error at compile time instead of an unpleasant surprise at runtime: If this code were allowed to run, the result of addOne("10") would be "101"! Having type annotations for your code does more than just prevent errors. It also provides a rich layer of information about your code that various tools can take advantage of. For instance, if you use a full-featured editor like VS Code (https://code. visualstudio. com), you can see type information as you work: Which type does x have? What arguments does someFunc expect? TypeScript’s popularity has exploded in recent years. In fact, in the 2023 State of JavaScript survey (https://2023. stateofjs. com), developers reported spending more time writing TypeScript code than writing JavaScript code directly. As TypeScript’s popularity has grown, the benefits of using it have increased, as more and more open-source libraries come with built-in type information. We’ll show you how to use TypeScript in Chapter 4. Throughout the book, example code will include type annotations when appropriate. 1.3.2 Linting and formatting Back in 2008, Douglas Crockford famously wrote an (amusingly short) book named JavaScript: The Good Parts. As the language has evolved, more and more good parts have been added—but the “bad parts,” features and usage patterns that tend to make code confusing, remain. Thankfully, there’s a tool that can tell you when you step into that dark territory: a linter. #A Defines a function that takes a number x and returns x + 1 #B Calls the function with a string, which will yield an error when the type checker runs const addOne = (x: number) => x + 1; #A addOne("10"); #B 10 © Manning Publications Co. To comment go to liveBook Licensed to THIAGO BANDEIRA <thiago@lar.ifce.edu.br>
Page
15
The most popular linter for JavaScript and TypeScript is ESLint (https://eslint. org), a powerful and highly customizable tool. ESLint can enforce as many rules as you like, ranging from simple mistake- catchers like no-unreachable (triggered if code exists after a return or other short-circuit keyword) to library-specific recommendations like react/no-deprecated (triggered if you call one of React’s deprecated methods). One simple linter rule that can help prevent common errors is no-shadow, which prohibits “shadowing” by declaring a variable with the same name as one that already exists in the outer scope. This code, for example, wouldn’t work as intended because of shadowing: You can also use linter rules to enforce code style rules. Want all comments in your project to be capitalized? Just enable the capitalized-comments rule. Stylistic rules often come with auto-fix functionality, allowing the linter to simply change the code (by capitalizing comments, for example) rather than showing an error. A close cousin of the linter is the formatter, a tool that makes code more readable by breaking up long lines of code into multiple lines, applying consistent indentation rules, and more. The most popular formatter for JavaScript and TypeScript is Prettier (https://prettier. io), and it’s been an enormous time-saver for developers: Instead of spending time formatting code by hand (or, worse, arguing with colleagues about how code should be formatted), Prettier takes care of it in the blink of an eye. We’ll show you how to use ESLint and Prettier in Chapter 4. #A The no-shadow ESLint rule would flag this line, because a variable called username is declared in the surrounding scope #B This line is a no-op, assigning the function argument to itself instead of modifying the username variable as intended let username = "Zork"; function logInAs(username) { #A username = username; #B } logInAs("Grue"); 11 © Manning Publications Co. To comment go to liveBook Licensed to THIAGO BANDEIRA <thiago@lar.ifce.edu.br>
Page
16
NOTE Any time we discuss a feature where a helpful ESLint rule is available, you’ll see this icon. 1.3.3 Testing Type checking and linting your code can catch a lot of errors before they happen, but they’re no substitute for actually running your code! A test framework lets you make assertions about what will happen when your code runs in order to verify its behavior. Ideally an application should have at least two kinds of tests: unit tests and end-to-end tests. A unit test verifies code’s behavior in isolation. Operations like network requests are “mocked” to provide hard-coded input, known as fixtures. Because they have no dependencies on other systems, unit tests run fast and aren’t prone to false positives from issues like network errors. Unit tests typically run in a server runtime like Node.js, with a simulated browser environment if necessary. Popular unit testing frameworks include Vitest, Jest, and Mocha. Recent versions of Node.js also have a built-in test runner. An end-to-end test verifies code’s behavior as part of a system. User input is simulated, but interactions with dependencies like databases and third-party APIs are real. For web apps, end-to- end tests typically run in a real browser against the deployed site. End-to-end tests can catch issues that unit tests can’t, because unit tests can’t account for all of the possible failure modes that occur when different parts of a system try to talk to each other. Popular end-to-end testing frameworks include Playwright and Cypress. 12 © Manning Publications Co. To comment go to liveBook Licensed to THIAGO BANDEIRA <thiago@lar.ifce.edu.br>
Page
17
Testing is absolutely essential for real-world applications. However, to keep the code example in this book succinct, we’ll be using a terser approach to verifying behavior: the console.assert function, which comes standard in all JavaScript environments. The general form of this function is as follows: The first parameter is a condition that you expect to be true. If the condition isn’t met, the error message is emitted to the console. Consider this, for example: These best-practice techniques, along with others you’ll learn along the way, will greatly improve your development experience. They’ll help you avoid common pitfalls and write code that’s easier to maintain, scale, and collaborate on. As you progress through this book, you’ll learn more about these techniques and develop the skills you need to build robust JavaScript applications. 1.4 Challenges facing JavaScript Ninjas As JavaScript’s success has grown, new challenges have arisen that you may need to face in your career. As you read through this book, keep these challenges in mind. 1.4.1 Performance While JavaScript engines have become more and more efficient over time, many websites that rely on JavaScript have become slower. Companies understand that having a sluggish website hurts their bottom line: Search engines penalize slow pages in their rankings, and potential customers who click a link will lose patience if it takes more than a few seconds to load. Turning a slow website into a fast one is a valuable (and measurable) service that a JavaScript Ninja is well-equipped to provide. The most obvious cause of slow performance on the web is code bloat, where an excessive amount of JavaScript is sent to the browser. To understand code bloat, consider the incentives faced by developers: It’s usually easier to write code that runs in the browser than to write code that runs on the server. It’s usually easier to implement a feature by adding an existing package to the project than it is to write it from scratch. It’s usually easier for a package author to combine a wide range of functionality in a single package than it is to write several modular packages with different pieces of that functionality. console.assert(condition, message); const a = 1; console.assert(a === 1, "a is not 1"); // No output const b = 22; console.assert(b === 2, "b is not 2"); // Emits "b is not 2" 13 © Manning Publications Co. To comment go to liveBook Licensed to THIAGO BANDEIRA <thiago@lar.ifce.edu.br>
Page
18
Taking the easy path at every step is a surefire recipe for sending a lot of unnecessary code to the browser. Even if that code never runs, it still needs to be downloaded and parsed, consuming precious bandwidth and CPU. In Chapter 14, we’ll look at some techniques for avoiding code bloat, such as tree shaking to remove extraneous code, production flags to keep debugging code from being deployed, and lazy loading to defer code until it’s needed. Of course, JavaScript performance goes beyond avoiding code bloat. Throughout the book, we’ll provide tips for writing efficient code that takes advantage of modern ECMAScript features. NOTE Any time we offer a performance tip, you’ll see this icon. 1.4.2 Collaboration at scale When you use a complex website or app, it’s very likely that you’re looking at the work of multiple teams. For instance, on an e-commerce site, you might see a navigation header built by one team, a product listing built by a second team, and a shopping cart built by a third. Dividing a product among multiple teams requires careful consideration of several tradeoffs. The expertise of a JavaScript Ninja can help guide companies to the right architecture before they invest too much time and effort in the wrong one. 14 © Manning Publications Co. To comment go to liveBook Licensed to THIAGO BANDEIRA <thiago@lar.ifce.edu.br>
Page
19
The simplest architecture is a single codebase that deploys as a single unit. At large scale, the shared codebase causes conflicts, particularly around dependencies. For example, imagine that one team wants to use a new version of React on the website. That means updating the entire codebase to make it compatible with the new React version—which could introduce bugs when run with the old React version. Not every team will be excited about having to put feature development on hold while they update their part of the codebase. To mitigate these issues, large-scale projects will usually adopt an architecture that divides the codebase into separate units that each team can update independently, including dependency changes. But that raises questions, such as: How do multiple teams independently deploy code that’s used on different parts of the same web page? In Chapter 14, we’ll take a look at module federation, a feature that can address some of these issues by allowing different parts of web pages to be deployed independently. (For more details on this pattern, check out Micro Frontends in Action by Michael Geers: https://www. manning.com/ books/micro- frontends- in-action.) And in Chapter 18 we’ll look at codemods, tools that can automate many code migrations. Codemods can greatly reduce the friction that occurs when teams need to coordinate code changes. 1.4.3 Dependency management You might have noticed a common theme among the previous sections on JavaScript challenges: Dependencies complicate everything! Beyond the problems we’ve already touched on, there are several more ways for dependencies to ruin your day: You might update a dependency version to pull in a bugfix, only to discover breaking changes in that dependency’s behavior, because package authors routinely ignore semantic versioning rules. You might install one version of a package and have an indirect dependency on another version, causing conflicts when both versions try to run in your application. You might want to use a package that’s published in ES module format, only to run into errors in an environment that expects CommonJS. (Or vice versa!) Every JavaScript developer’s worst nightmare: You might fall victim to a supply chain attack, where a third-party package you use becomes a vector for malicious code, which can either run directly on your development machine or wherever you deploy your project. We’ll cover some of these issues in Chapter 3 when we introduce npm. And we’ll cover ES modules vs. CommonJS in Chapter 14. One of the nice things about JavaScript is that before you add any third-party code to your project, you can read the code for yourself. As a JavaScript Ninja, you’ll be well equipped to anticipate any problems that could arise from adding a dependency to your project and to estimate the effort needed to implement the functionality you need yourself. 15 © Manning Publications Co. To comment go to liveBook Licensed to THIAGO BANDEIRA <thiago@lar.ifce.edu.br>
Page
20
1.5 Summary JavaScript is everywhere. In addition to being the language of the web, it can be used to build applications for every major computing platform. The JavaScript language has evolved dramatically over time, and continues to improve every year. However, depending on where you expect your code to run, you may need to transpile your code to take advantage of the latest features. The vast majority of modern JavaScript projects are built using an open-source framework. Choosing the right framework for your project will give you a critical edge. Best practices such as type checking, linting, and testing are key to writing robust, reliable code. Some of the biggest challenges facing JavaScript developers are performance, collaboration at scale, and dependency management. This book focuses on the core mechanics of the JavaScript language. Mastering those fundamentals will help you to architect, write, and maintain robust JavaScript applications. 16 © Manning Publications Co. To comment go to liveBook Licensed to THIAGO BANDEIRA <thiago@lar.ifce.edu.br>