Explore 1.5M+ audiobooks & ebooks free for days

From $11.99/month after trial. Cancel anytime.

JavaScript: The New Toys
JavaScript: The New Toys
JavaScript: The New Toys
Ebook1,180 pages11 hours

JavaScript: The New Toys

Rating: 0 out of 5 stars

()

Read preview

About this ebook

All of JavaScript's newest features, in depth, made easy to understand.

JavaScript is a rapidly changing language and it can be challenging to keep up with all the new toys being added. JavaScript: The New Toys explores the newest features of the world's most popular programming language while also showing readers how to track what's coming next. After setting the stage by covering who manages the process of improving JavaScript, how new features get introduced, terminology, and a high-level overview of new features, it details each new or updated item in depth, with example uses, possible pitfalls, and expert recommendations for updating old habits in light of new features.  JavaScript: The New Toys:

  • Covers all the additions to JavaScript in ES2015-ES2020 plus a preview of what's coming next
  • Explores the latest syntax: nullish coalescing, optional chaining, let and const, class syntax, private methods, private fields, new.target, numeric separators, BigInt, destructuring, default parameters, arrow functions, async functions, await, generator functions, ... (rest and spread), template literals, binary and octal literals, ** (exponentiation), computed property/method names, for-of, for-await-of, shorthand properties, and others
  • Details the new features and patterns including modules, promises, iteration, generators, Symbol, Proxy, reflection, typed arrays, Atomics, shared memory, WeakMap, WeakSet, and more
  • Highlights common pitfalls and explains how to avoid them
  • Shows how to follow the improvements process and even participate in the process yourself
  • Explains how to use new features even before they're widely supported

With its comprehensive coverage and friendly, accessible style, JavaScript: The New Toys provides an invaluable resource for programmers everywhere, whether they work in web development, Node.js, Electron, Windows Universal Apps, or another JavaScript environment.

LanguageEnglish
PublisherWiley
Release dateJun 30, 2020
ISBN9781119367963
JavaScript: The New Toys

Related to JavaScript

Related ebooks

Programming For You

View More

Reviews for JavaScript

Rating: 0 out of 5 stars
0 ratings

0 ratings0 reviews

What did you think?

Tap to rate

Review must be at least 10 words

    Book preview

    JavaScript - T. J. Crowder

    INTRODUCTION

    THIS BOOK IS FOR ANY JAVASCRIPT (OR TYPESCRIPT) programmer who wants to get up to speed on the latest features added to JavaScript in the last few years, and who wants to know how to stay informed as the language continues to evolve and grow. You can find nearly all of the information in this book somewhere on the web if you look hard enough and you’re cautious about what sites you trust; this book provides all of the technical details in one place, along with showing you how to keep track of the changes as they continue to be made. You can continue to stay up-to-date via the book's website at https://thenewtoys.dev, where I continue covering new features as they're added.

    WHAT DOES THIS BOOK COVER?

    Here's a glance at what's in each chapter:

    Chapter 1: The New Toys in ES2015–ES2020, and Beyond — begins with introducing various players in the JavaScript world and some important terminology. Then it describes the definition of The New Toys for the purposes of the book; covers the way new features are added to JavaScript, how that process is managed, and by whom; and explains how to follow and participate in that process. It finishes by introducing some tools for using the new toys in older environments (or using the very newest toys in current environments).

    Chapter 2: Block-Scoped Declarations: let and const — covers the new declaration keywords let and const and the new scoping concepts they support, including in-depth coverage of scoping in loops, particularly the new handling in for loops.

    Chapter 3: New Function Features — covers the variety of new features related to functions: arrow functions, default parameter values, rest parameters, the name property, and various syntax improvements.

    Chapter 4: Classes — covers the new class feature: the basics, what it is and what it isn't, subclassing, super, subclassing built-ins like Array and Error, and the new.target feature. Chapter 4 leaves coverage of private fields and other features making their way through the proposals process to Chapter 18.

    Chapter 5: New Object Features — covers computed property names, shorthand properties, getting and setting an object's prototype, the new Symbol type and how it relates to objects, method syntax, property order, property spread syntax, and a host of new object functions.

    Chapter 6: Iterables, Iterators, for-of, Iterable Spread, Generators — covers iteration, a powerful new tool for collections and lists, and generators, a powerful new way to interact with functions.

    Chapter 7: Destructuring — covers this important new syntax and how to use it to extract data from objects and from arrays and other iterables, with defaults, deep picking, and more.

    Chapter 8: Promises — dives deep into this important new tool for coping with asynchronous processes.

    Chapter 9: Asynchronous Functions, Iterators, and Generators — goes into detail about the new async/ await syntax that lets you use familiar logical flow structures with asynchronous code, as well as how asynchronous iterators and generators work and the new for-await-of loop.

    Chapter 10: Templates, Tag Functions, and New String Features — describes template literal syntax, tag functions, and lots of new string features like better Unicode support, updates to familiar methods, and lots of new methods.

    Chapter 11: New Array Features, Typed Arrays — covers the wide range of new array methods, various other array updates, typed arrays such as Int32Array, and advanced features for interacting with typed array data.

    Chapter 12: Maps and Sets — teaches you all about the new keyed collections Map and Set, and also their weak cousins WeakMap and WeakSet.

    Chapter 13: Modules — is a deep dive into this exciting and powerful way to organize your code.

    Chapter 14: Reflection—Reflect and Proxy — covers the powerful new dynamic metaprogramming features of the Reflect and Proxy objects and how they relate to one another.

    Chapter 15: Regular Expression Updates — describes all of the updates to regular expressions that have been done the past several years such as new flags, named capture groups, lookbehind assertions, and new Unicode features.

    Chapter 16: Shared Memory — covers the complex and potentially difficult area of sharing memory across threads in a JavaScript program, including covering SharedArrayBuffer and the Atomics object as well as fundamental concepts and notes on pitfalls.

    Chapter 17: Miscellany — covers a wide range of things that didn't quite fit in elsewhere: BigInt, the new integer literal syntaxes (binary, new octal), optional catch bindings, new math methods, the exponentiation operator, various additions to the Math object (no pun!), tail call optimization, nullish coalescing, optional chaining, and Annex B (browser-only) features defined for compatibility reasons.

    Chapter 18: Upcoming Class Features — takes us a bit into the future, describing enhancements to classes that haven't quite been finished yet but which are far along in the process: public field declarations, private fields, and private methods.

    Chapter 19: A Look Ahead … — winds up with looking forward to some further enhancements currently in the pipeline: top-level await, WeakRefs and cleanup callbacks, RegExp match indices, Atomics.asyncWait, a couple of new syntax features, legacy RegExp features, and various upcoming standard library additions.

    Appendix: Fantastic Features and Where to Find Them — (with apologies to J.K. Rowling) provides lists of the new toys and tells you which chapter covers each of them. The lists are: Features in Alphabetical Order; New Fundamentals; New Syntax, Keywords, Operators, Loops, and Similar; New Literal Forms; Standard Library Additions and Changes; and Miscellaneous.

    WHO SHOULD READ THIS BOOK

    You should read this book if:

    You have at least a basic understanding of JavaScript, and

    You want to learn the new features added in the last several years.

    This isn't an academic book just for experts. It's a pragmatic book for everyday JavaScript programmers.

    Almost anyone picking up this book is already going to know some of what's in it, and almost no one picking up this book will already know all of it. Maybe you're already clear on the basics of let and const, but you don't quite get async functions yet. Maybe promises are old hat to you, but you saw some syntax you didn't recognize in some modern code you ran across. You'll find all of it from ES2015 through ES2020 (and beyond) covered here.

    HOW TO USE THIS BOOK

    Read Chapter 1. Chapter 1 defines a lot of terms I use throughout the rest of the book. Skipping Chapter 1 will likely trip you up.

    After that, you have a choice to make: read the chapters in order, or jump around?

    I've laid out the chapters in the order I have for a reason; and I build on the content of earlier chapters in later chapters. For instance, you'll learn about promises in Chapter 8, which is important to understanding async functions in Chapter 9. Naturally, I suggest reading them in the order I've arranged them. But I bet you're a smart person who knows their own mind; if you jump around instead, you'll be just fine.

    I do suggest that you read—or at least skim—all of the chapters (with the possible exception of Chapter 16; more on that in a minute). Even if you think you know a feature, there's probably something here that you don't know, or only think you know. For instance: maybe you're planning to skip Chapter 2 because you already know all there is to know about let and const. Maybe you even know why

    for (let i = 0; i < 10; ++i) { /*…*/     setTimeout(() => console.log(i)); }

    creates ten different variables called i, and why using

    let a = ay; var b = bee;

    at global scope creates a window.b property but doesn't create a window.a property. Even if you do, I'd skim Chapter 2 just to be sure there aren't other wrinkles you haven't picked up.

    Chapter 16 is a bit of a special case: it's about sharing memory between threads. Most JavaScript programmers don't need to share memory between threads. Some of you will, and that's why Chapter 16 is there, but most won't and if you're in that category, just tuck away the fact that if you think you need shared memory at some point in the future you can come back and learn about it in Chapter 16. That's just fine.

    Beyond all that: run the examples, experiment with them, and above all enjoy yourself.

    HOW TO CONTACT THE AUTHOR

    I'd love to hear your feedback. Please feel free to reach out:

    If you think you've found an error, please write to [email protected].

    You can download the examples and listings at https://thenewtoys.dev/bookcode or https://www.wiley.com/go/javascript-newtoys.

    You can ask me questions and/or talk with other readers on https://thenewtoys.dev.

    You can reach me on Twitter at @tjcrowder.

    I'm usually hanging out in the JavaScript tag on https://stackoverflow.com/.

    Enjoy!

    1

    The New Toys in ES2015–ES2020, and Beyond

    WHAT'S IN THIS CHAPTER?

    Definitions, who's who, and terminology

    Explanation of JavaScript versions (ES6? ES2020?)

    What are the new toys?

    The process driving new JavaScript features

    Tools for using next-generation JavaScript

    CODE DOWNLOADS FOR THIS CHAPTER

    You can download the code for this chapter at https://thenewtoys.dev/bookcode or https://www.wiley.com/go/javascript-newtoys.

    JavaScript has changed a lot in the last few years.

    If you were an active JavaScript developer in the 2000s, for a while there you could be forgiven for thinking JavaScript was standing still. After the 3rd Edition specification in December 1999, developers were waiting fully 10 years for the next edition of the specification. From the outside, it seemed like nothing was happening. In fact, lots of work was being done; it just wasn't finding its way into the official specification and multiple JavaScript engines. We could (but won't) spend an entire chapter—if not a book—on what various different groups in important positions vis-à-vis JavaScript were doing and how they couldn't agree on a common path forward for some time. The key thing is that they ultimately did agree on a way forward, at a fateful meeting in Oslo in July 2008 after much advance negotiation. That common ground, which Brendan Eich (creator of JavaScript) later called Harmony, paved the way for the 5th Edition specification in December 2009 (the 4th Edition was never completed), and laid the basis for ongoing progress.

    And my, how things have progressed.

    In this chapter, you'll get an overview of the new features since 2009 (which the rest of the book covers in detail). You'll learn who's in charge of moving JavaScript forward, what process is now used to do so, and how you can keep track of what's coming and get involved if you like. You'll learn about tools you can use to write modern JavaScript today, even if you have to target environments that haven't kept up.

    DEFINITIONS, WHO'S WHO, AND TERMINOLOGY

    To talk about what's going on with JavaScript, we need to define some names and common terminology.

    Ecma? ECMAScript? TC39?

    What we think of as JavaScript is standardized as ECMAScript by Ecma International,¹ a standards organization responsible for multiple computing standards. The ECMAScript standard is ECMA-262. The people in charge of the standard are members of Ecma International Technical Committee 39 (TC39), charged with Standardization of the general purpose, cross platform, vendor-neutral programming language ECMAScript. This includes the language syntax, semantics, and libraries and complementary technologies that support the language.² They manage other standards as well, such as the JSON Syntax Specification (ECMA-404), and notably the ECMAScript Internationalization API Specification (ECMA-402).

    In this book and in common usage, JavaScript is ECMAScript and vice versa. Sometimes, particularly during the decade of different groups doing different things, JavaScript was used to specifically mean the language Mozilla was developing (which had several features that either never made it into ECMAScript, or changed markedly before they did), but since Harmony that usage is increasingly outdated.

    ES6? ES7? ES2015? ES2020?

    Having all these various abbreviations can be confusing, not least because some have edition numbers but others have years. This section explains what they are and why there are two kinds of them.

    Up through the 5th Edition, TC39 referred to versions of the specification via their edition number. The full title of the 5th Edition spec is:

    Standard ECMA-262

    5th Edition / December 2009

    ECMAScript Language Specification

    Since ECMAScript 5th Edition is a bit of a mouthful, saying ES5 was the natural thing to do.

    Starting with the 6th Edition in 2015, TC39 adopted a continual improvement process where the specification is a living editor's draft³ with annual snapshots. (More about that later in this chapter.) When they did that, they added the year to the language name:

    Standard ECMA-262

    6th Edition / June 2015

    ECMAScript® 2015 Language Specification

    So the ECMAScript 6th Edition standard (ES6) defines ECMAScript 2015, or ES2015 for short. Prior to publication, ES6 became a buzzword of its own and is still in common usage. (Unfortunately, it's often used inaccurately, referring not just to features from ES2015, but also to features that came afterward in ES2016, ES2017, and so on.)

    That's why there are two styles, the style using the edition (ES6, ES7, …) and the style using the year (ES2015, ES2016, …). Which you use is up to you. ES6 is ES2015 (or sometimes, incorrectly, ES2015+), ES7 is ES2016, ES8 is ES2017, and so on through (as of this book's release) ES11, which is ES2020. You'll also see ESnext or ES.next, which are sometimes used to refer to upcoming changes.

    In this book, I use what I see as the emerging consensus: the old style for ES5 and earlier, and the new style for ES2015 and later.

    All of that said, although I will usually call out the specific edition where a feature was introduced, the fact that Array.prototype.includes is from ES2016 and Object.values is from ES2017 doesn't really matter very much. What matters more is what's actually supported in your target environments and whether you need to either refrain from using a specific feature, or transpile and/or polyfill it. (More on transpiling and polyfilling later in the Using Today's Toys in Yesterday's Environments, and Tomorrow's Toys Today section.)

    JavaScript Engines, Browsers, and Others

    In this book I'll use the term JavaScript engine to refer to the software component that runs JavaScript code. A JavaScript engine must know how to:

    Parse JavaScript,

    Either interpret it or compile it to machine code (or both), and

    Run the result within an environment that works as described by the specification.

    JavaScript engines are also sometimes called virtual machines, or VMs for short.

    One usual place you find JavaScript engines is in web browsers, of course:

    Google's Chrome browser uses their V8 engine (also used in Chromium, Opera, and Microsoft Edge v79 and later), except on iOS (more on that in a bit).

    Apple's Safari browser (for Mac OS and for iOS) uses their JavaScriptCore engine.

    Mozilla's Firefox uses their SpiderMonkey engine, except on iOS.

    Microsoft's Internet Explorer uses their JScript engine, which is increasingly out of date as it only gets security fixes.

    Microsoft Edge v44 and earlier (Legacy Edge) uses Microsoft's Chakra engine. In January 2020, Edge v79 was released, which is based on the Chromium project and uses the V8 engine, except on iOS. (The version number jumped from 44 to 79 to align with Chromium.) Chakra is still used in various products that use the Microsoft WebView control, such as Microsoft Office JavaScript add-ins, though it may be replaced at some stage. (WebView2, using Chromium Edge, is in developer preview as of early 2020.)

    Chrome, Firefox, Edge, and other browsers running on Apple's iOS operating system for iPad and iPhone can't currently use their own JavaScript engines, because to compile and run JavaScript (rather than just interpreting it), they have to allocate executable memory, and only Apple's own iOS apps are allowed to do that, not ones from other vendors. So Chrome and Firefox (and others) have to use Apple's JavaScriptCore instead on iOS even though they use their own engine on desktop and Android. (At least, that's true for now; the V8 team added an interpreter only mode to V8 in 2019, which means Chrome and others using V8 could use that mode on iOS, since it doesn't have to use executable memory.) In this book, if I say something is supported by Chrome or supported by Firefox, I'm referring to the non-iOS versions using V8 or SpiderMonkey, respectively.

    JavaScript engines are also used in desktop applications (Electron,⁴ React Native,⁵ and others), web servers and other kinds of servers (often using Node.js⁶), non-web applications, embedded apps—just about everywhere.

    WHAT ARE THE NEW TOYS?

    For this book, the new toys are the new features added to JavaScript in ES2015 through ES2020 (and previewing some that are coming soon). The language has come a long way in those six updates. The following is a general overview. (Appendix A has a more complete list of changes.) Some of the terms on the list may be unfamiliar; don't worry about that, you'll learn them across the course of the book.

    Opt-in block scope ( let, const): Narrower scoping for variables, clever handling of for loop body scope, variables whose value cannot change ( const)

    Arrow functions: Lightweight, concise functions that are particularly useful for callbacks since they close over this rather than having their own this value that's set when they're called

    Improvements to function parameters: Default values; parameter destructuring, rest parameters, trailing commas

    Iterable objects: Well-defined semantics for creating and consuming iterable objects (such as arrays and strings), in-language iteration constructs ( for-of, for-await-of); generator functions for generating sequences that can be iterated (including asynchronous sequences)

    Spread syntax: Spreading out array (or other iterable) entries into new arrays, spreading out object properties into new objects, and spreading out iterable entries into discrete function arguments; particularly useful for functional programming, or anywhere immutable structures are used

    Rest syntax: Gathering together the rest of an object's properties, an iterable's values, or a function's arguments into an object or array

    Other syntax improvements: Allowing a trailing comma in the argument list when calling a function; omitting unused identifiers in catch clauses; new-style octal literals; binary literals; separator characters in numeric literals; and more

    Destructuring: Picking out the values from arrays/objects in a concise way that mirrors object and array literal syntax

    class: Markedly simpler, declarative syntax for creating constructor functions and associated prototype objects, while preserving JavaScript's inherent prototypical nature

    Asynchronous programming improvements: Promises, async functions and await; these markedly decrease callback hell

    Object literal improvements: Computed property names, shorthand properties, method syntax, trailing commas after property definitions

    Template literals: A simple, declarative way to create strings with dynamic content, and go beyond strings with tagged template functions

    Typed arrays: Low-level true arrays for using native APIs (and more)

    Shared memory: The ability to genuinely share memory between JavaScript threads (including inter-thread coordination primitives)

    Unicode string improvements: Unicode code point escape sequences; support for accessing code points instead of code units

    Regular Expression improvements: Lookbehind assertions; named capture groups; capture indices; Unicode property escapes; Unicode case insensitivity

    Maps: Key/value collections where the keys don't have to be strings

    Sets: Collections of unique values with well-defined semantics

    WeakMap, WeakSet, and WeakRef: Built-ins for holding only weak references to objects (allowing them to be garbage collected)

    Standard library additions: New methods on Object, Array, Array.prototype, String, String.prototype, Math, and others

    Support for dynamic metaprogramming: Proxy and Reflect

    Symbols: Guaranteed-unique values (particularly useful for unique property names)

    BigInt: Arbitrary precision integers

    Many, many others

    All these new features, in particular the new syntax, could be overwhelming. Don't be concerned! There's no need to adopt new features until/unless you're ready to and have need of them. One of the key principles TC39 adheres to is Don't break the web. That means that JavaScript must remain web compatible—that is, compatible with the huge body of code that already exists in the world today.⁷ If you don't need a new feature, or you don't like a new feature, you don't have to use it. Your old way of doing whatever that feature does will always continue to work. But in many cases, you're likely to find there's a compelling reason for the new feature, in particular new syntax features: they make something simpler and less error-prone to write and understand, or—in the case of Proxy and WeakMap/ WeakSet, shared memory, and others—they enable things that mostly couldn't be done without them.

    For space reasons, this book only covers the new toys in the JavaScript specification itself, ECMA-262. But there are some exciting new toys in the ECMAScript Internationalization API Specification⁸ as well, ECMA-402, which is well worth reading. You can find coverage of ECMA-402 on this book's website at https://thenewtoys.dev/internationalization.

    HOW DO NEW TOYS GET CREATED?

    In this section you'll learn who's in charge of moving JavaScript forward, what process they use to do so, and how to follow and participate in that process.

    Who's in Charge

    You learned earlier that Ecma International's Technical Committee 39 (TC39) is in charge of creating and releasing updated specifications for the ECMAScript standard. The committee is made up of JavaScript developers, framework authors, large website authors/maintainers, programming language researchers, representatives from all the major JavaScript engines, influential JavaScripters, and other stakeholders in JavaScript's success and future. They have regular meetings, historically six per year for three days. To participate in meetings as a member, your organization can join Ecma.⁹ TC39 navigates the difficult waters of developers' desires, implementation complexity, security concerns, backward compatibility, and many other design inputs to bring new and useful features to the JavaScript community.

    To ensure that the committee works as part of the community rather than separate from it, TC39 maintains the ecma262 GitHub repository¹⁰ with the up-to-date specification (browsable at https://tc39.es/ecma262/) and a proposals repository¹¹ for proposals going through the TC39 process described in the next section. Some members are also active on the TC39 Discourse group.¹² Notes of the TC39 meetings and associated materials (slides, etc.) are posted to https://github.com/tc39/notes.

    You can learn more about TC39, and how to get involved, at https://tc39.es/. You can also learn more about how TC39 works at https://github.com/tc39/how-we-work.

    The Process

    TC39 adopted a well-defined process in November 2013 and first published it in January 2014. The process has multiple stages that TC39 moves proposals through (Stage 0 through Stage 4), with clear expectations at each stage, and clear criteria for moving from one stage to the next. Once a proposal meets the criteria to move to the next stage, the committee decides by consensus whether to move it forward.

    It's worth reading the process document itself,¹³ but in brief, the stages are:

    Stage 0 – Strawperson: Someone's had an idea they think is worth considering so they've thought about it, written it up a bit, and put it forward. (This stage almost isn't a stage, and the term has been applied to different things at different times.) If the person putting it forward isn't a TC39 member, they need to register as a non-member contributor¹⁴ (which anyone can do). Some Stage 0 proposals end up being listed in the TC39 proposals repository, but others don't; typically it's just ones that have gained a potential champion who is a committee member. If a Stage 0 proposal gains enough interest, a TC39 member may get it added to the agenda for a TC39 meeting to be discussed and considered for Stage 1.

    Stage 1 – Proposal: Once a proposal has been put to the committee and there's consensus to investigate it further, the committee moves it to Stage 1 with a champion to guide it through the process. If there isn't already a GitHub repo for it, the originator or champion or another interested party creates one. Then members of the community (whether on the committee or not) discuss it, develop it further, research similar technology in other languages or environments, refine the scope, figure out the general form of a solution, and generally flesh out the idea. As a result of this work, it might turn out that the benefits aren't worth the costs, or it could be that the idea needs to be broken up and added to other proposals, etc. But if the proposal has legs, the people involved (who may have changed over time) will put together some initial draft specification language, API, and semantics and put it forward to TC39 to be considered for Stage 2.

    Stage 2 – Draft: When it's ready, a Stage 1 proposal can be presented at a TC39 meeting to be considered for Stage 2. This means seeking consensus that the proposal should go ahead, with the expectation it will likely go through the entire process. Stage 2 is the stage where the community refines the precise syntax, semantics, API, and so on and describes the solution in detail using formal specification language. Often, polyfills and/or Babel plugins get created at this stage to enable experimentation with real use. Depending on its scope, a proposal may stay at Stage 2 for some time as the details are worked out.

    Stage 3 – Candidate: Once the team has matured a proposal to a final draft form and created formal specification language for it, the champion can put it forward for Stage 3. This means seeking consensus that the proposal is ready to be implemented in JavaScript engines. At Stage 3, the proposal itself is nearly stable. Changes at this point are expected to be limited to implementation-driven feedback, such as corner cases discovered during implementation, web-compatibility issues, or difficulty of implementation.

    Stage 4 – Finished: At this point, the feature is complete and is ready to be added to the editor's draft at https://tc39.es/ecma262/. To reach this final stage, the feature must have acceptance tests in TC39's test262 test suite;¹⁵ at least two distinct compatible implementations that pass the tests (for instance, shipping in V8 in Chrome Canary and SpiderMonkey in Firefox Nightly, or SpiderMonkey in Firefox and JavaScriptCore in Safari Tech Preview, etc.). Once those criteria are met, the final step to reach Stage 4 is for the team working on the feature to send a pull request to the ecma262 repository to incorporate the specification changes into the editor's draft, and the ECMAScript editor group to accept that PR.

    That's the process proposals go through to become part of JavaScript. But not every change is a proposal. Smaller changes can be made through consensus at TC39 meetings based on a pull request to the specification. For instance, the output of Date.prototype.toString changed between ES2017 and ES2018 (see Chapter 17) as the result of consensus on a pull request, not a staged proposal. Often, these are editorial changes, or changes reflecting the reality of what JavaScript engines already do but which isn't in the specification, or changes to what the specification says to do that have been agreed on because TC39 believes they're both desirable and web compatible (won't break a large amount of existing code), such as the change in ES2019 making Array.prototype.sort a stable sort (see Chapter 11). If you want to know what changes are being considered or made in this way, watch the needs consensus label in the https://github.com/tc39/ecma262 repo (and similarly the https://github.com/tc39/ecma402 repo for ECMA-402 changes). To find ones that have been completed, look in the has consensus, editorial change, and/or normative change labels. At some point there may be a more formal process for these needs consensus changes, but for now this is how you watch for them.

    Getting Involved

    If you see a proposal that interests you and you want to get involved, when should you do that? What should that involvement look like?

    One key thing is: get involved early. Once a proposal has reached Stage 3, only critical changes based on implementation experience are typically considered. The best times to get involved are at Stages 0, 1, and 2. That's when you can provide insight based on your experience, help define semantics, try out what's being proposed using tools like Babel (which you'll learn about in a later section), etc. It's not that you can't find a useful role in a Stage 3 proposal (there are sometimes tasks to be done in terms of firming up specification text or helping write developer documentation), just be aware that suggesting changes at Stage 3 usually isn't useful unless you're one of the people implementing it in a JavaScript engine and you run into a problem.

    So: you've found a proposal you want to get involved in; what now? It's up to you, but here are some suggestions:

    Do your research. Read the proposal's explainer (the README.md that's linked from the TC39 list of proposals) and other documents closely and carefully. If they refer to prior art (such as a similar feature in another language), it's useful to read up on that prior art. If there's initial specification text, read it. (This guide may be helpful there: https://timothygu.me/es-howto/.) You want to be sure that the input you provide is well-informed.

    Try the feature out! Even if you can't use it yet, you can write speculative code (code you can't run, but can think about) to consider how well the proposal is solving the problem it sets out to solve. If there's a Babel plugin for it, try writing and running code. See how the feature works for you and provide feedback on it.

    Look for ways you can help. Aside from suggestions and feedback, there are lots of ways to help with a proposal. For instance, you can look for issues where a consensus has been reached about what to do, but no one's had the time to do it (researching prior art, updating the explainer, updating spec text). You could do those updates, if it's something you're comfortable doing. You can coordinate with the proposal authors by discussing contributions in GitHub issues.

    When getting involved, remember to treat everyone with respect and to be friendly, patient, inclusive, and considerate. Be careful with the words you choose. You're more likely to have influence by treating people well and demonstrating a collaborative spirit than by seeming cross, dismissive, or obstructive. Note that TC39's meetings and online spaces used to develop proposals are governed by a Code of Conduct.¹⁶ Please refer improper conduct to TC39's Code of Conduct committee (see the linked document).

    KEEPING UP WITH THE NEW TOYS

    You don't have to keep up with the new toys. As I've mentioned, your old way of doing things isn't going away. But if you're reading this book, I suspect you want to.

    As you were reading through the process earlier, one thing that may have stricken you is that the process guarantees that new features make it into the real world before they are added to the specification. In contrast, when ES5 came out in 2009 and ES2015 came out in 2015, most of the features they described didn't exist (in the form described) in any JavaScript engine then shipping. If the spec follows the new features rather than the other way around, how do you keep up with what features are coming next? Here are some ways:

    Watch the proposals repository on GitHub (https://github.com/tc39/proposals). If something gets to Stage 3, it's probably going to get added within a year or two. Even Stage 2 features are at least likely to get added eventually, though any feature can be rejected by TC39 at nearly any stage.

    Read the meeting notes of TC39 meetings posted at https://github.com/tc39/notes.

    Participate in the TC39 Discourse group (https://es.discourse.group/).¹⁷

    Pay attention to what's going on with the tools discussed in the next section.

    You can also follow along at https://thenewtoys.dev, where I continue coverage of what's coming next.

    USING TODAY'S TOYS IN YESTERDAY'S ENVIRONMENTS, AND TOMORROW'S TOYS TODAY

    In terms of just learning the features covered in this book, you don't need to worry about dealing with environments that don't support them; nearly everything covered (other than Chapters 18 and 19) is supported by current versions of the Chrome, Firefox, Safari, and Edge desktop browsers and Node.js at least. Simply use one of those to run the code.

    USING NODE.JS TO RUN EXAMPLES

    By default, when you run a script with Node.js, like this:

        node script.js

    it runs it at module scope, not global scope. A few examples in this book demonstrate things that only happen at global scope, so they won't work when you run the code that way.

    For those examples, either use a browser to run the code, or use Node.js's read/evaluate/print loop (REPL) instead. To use the REPL, you don't specify the script file to run as an argument to the node command. Instead, you redirect the script into it using the < operator (this works both on Unix/Linux/Mac OS systems and on Windows):

        node < script.js

    I'll remind you to do this when an example needs to be run at global scope.

    At some stage, though, you're likely to want to use new features in an environment that doesn't support them. For instance, most JavaScript development still targets web browsers, and the JavaScript engines in different browsers get new features at different times (if at all—Internet Explorer doesn't support any of the new features discussed in this book,¹⁸ but still has some global market share as of this writing, particularly in government and large company intranets).

    This was an issue when ES5 was released, since very little of it was implemented in any shipping JavaScript engine at the time. But most of ES5 was new standard library features, rather than significant syntax changes, so those could be polyfilled (added by including an extra script that provided the missing objects/functions) using various projects like es5-shim.js,¹⁹ core-js,²⁰ es-shims,²¹ or similar. During the development of ES2015 in 2010 through 2015, though, it was clear that real-world experience of new syntax was required to do a good job of developing that syntax, but JavaScript implementations didn't have the new syntax yet—an apparent catch-22.

    Tool builders to the rescue! They created tools like Traceur²² and Babel²³ (formerly 6to5), which take source code using the new syntax as input, convert it to use older syntax, and output that older-style code (along, optionally, with polyfills and other runtime support functions). Similarly, TypeScript²⁴ supported major parts of what would become ES2015 well before the specification was completed. These tools let you write new-style code, but convert it to old-style code before delivering it to old environments. This conversion process is variously called compiling or transpiling. This was initially handy for feedback on the JavaScript improvements being planned for ES2015, but even when ES2015 came out it was a useful way to write new-style code if you were planning to run it in an environment without the new features.

    As of this writing, Traceur has gone quiet, but Babel is in use by a large fraction of JavaScript developers worldwide. Babel has transforms for nearly all features that are in the process, even ones at Stage 1, which may change markedly before progressing. (So use those at your own risk. Stage 3 onward are fairly safe to use.) You select the transform plugins you want to use, write your code using those features, and Babel produces code you can use in environments without those features.

    Transpiling an Example with Babel

    In this section, we'll take a quick look at using Babel to transpile code using an ES2015 feature, called an arrow function, into ES5-compatible code that works on IE11. But this is just an example; you could just as easily use Babel to transform code using a Stage 3 feature not yet present in any shipping JavaScript engine into ES2020-compatible code. Babel also supports some transforms that aren't in the process at all, such as JSX²⁵ (used in some JavaScript frameworks, notably React²⁶). The truly adventurous can write their own transform plugins just for use on their projects!

    To install Babel, you'll want Node.js and npm (Node Package Manager). If you don't already have those installed on your system, either:

    Go to https://nodejs.org/ and use the appropriate installer/package for your system to install it; or

    Use the Node Version Manager, which provides a handy way to install Node versions and to switch between them: https://github.com/nvm-sh/nvm

    npm comes bundled with Node.js, so you don't have to install it separately.

    Once you have them installed:

    Create a directory for this example (for instance, example in your home directory).

    Open a command prompt/terminal window and change to the directory you just created.

    Use npm to create a package.json file: Type

    npm init

    and press Enter. npm will ask a series of questions; answer the questions as you like (or just press Enter in response to all of them). When done, it will write out package.json to your example directory.

    Next, install Babel. (The following steps are from going to https://babeljs.io/docs/setup/#installation and then clicking the CLI button; you might want to check there for updates.) Type

    npm install --save-dev @babel/core @babel/cli

    and press Enter. npm will download and install Babel, its command-line interface, and all of its dependencies into your example project. (You may get a warning related to a module called fsevents and/or some deprecation warnings; that's okay.)

    At this point, you could start using Babel by calling it directly, but let's make it easier by adding an npm script entry to package.json. Open package.json in your favorite editor. If there isn't a top-level scripts entry, create one (but current versions of npm will have included one with a test script that shows an error). Within the scripts entry, add this setting:

    build: babel src -d lib

    Now your package.json should look something like Listing 1-1. (Yours may still have a test entry in scripts; that's fine. It also may have a different license, I always change the default to MIT.) Make sure to save the file.

    LISTING 1-1: Example package.json—package.json

    {   name: example,   version: 1.0.0,   description: ,   main: index.js,   scripts: {     build: babel src -d lib   },   author: ,   license: MIT,   devDependencies: {     @babel/cli: ^7.2.3,     @babel/core: ^7.2.2   } }

    Babel is highly modular. Although we've installed it, we haven't told it to do anything yet. For this example, we'll use one of its presets to tell it to transform ES2015 code to ES5 code, by installing and then configuring the preset. To install the preset, type

    npm install --save-dev babel-preset-env

    and press Enter. In the next step we'll configure it.

    Now we need to create a configuration file for Babel, .babelrc (note the leading dot). Create the file with these contents (or use the one from the chapter downloads):

    presets: [    [      env,      {        targets: {          ie: 11        }      }    ]  ]}

    That configuration tells Babel to use its env preset, which the Babel documentation describes as … a smart preset that allows you to use the latest JavaScript without needing to micromanage which syntax transforms … are needed by your target environment(s). In this configuration, setting the target ie: 11 tells the env preset that you're targeting IE11, which is appropriate for the following example. For your real use, you'll want to look at the documentation for the env preset²⁷ and/or other presets or plugins you may want to use instead.

    That's it for the Babel setup for this example. Now let's create some code for it to transpile. Create a subdirectory of your example directory called src, and create a file in it called index.js with the contents of Listing 1-2. (At the end of the process, I'll show you a list of what files should be where, so don't worry too much if you're slightly unsure; just create the file and you can move it if it ends up in the wrong place.)

    LISTING 1-2: ES2015 transpiling example input—index.js

    var obj = {     rex: /\d/,     checkArray: function(array) {         return array.some(entry => this.rex.test(entry));     } }; console.log(obj.checkArray([no, digits, in, this, array])); // false console.log(obj.checkArray([this, array, has, 1, digit]));  // true

    The code in Listing 1-2 uses just one ES2015+ feature: an arrow function, the entry => this.rex.test(entry) within the call to some, which I've highlighted in the code. (Yes, that's really a function.) You'll learn about arrow functions in Chapter 3. The brief, incomplete version is that they offer a concise way to define a function (as you can see) and that they close over this just like closing over a variable (rather than having this set by how they're called). When obj.checkArray(…) is called, this within the call refers to obj even within the some callback, so this.rex refers to the rex property on obj. That wouldn't be true if the callback were a traditional function.

    At this point, your example directory should have these contents:

    example/ +−− node_modules/ |  +−− (various directories and files) +−− src/ |  +−− index.js +−− .babelrc +−− package.json +−− package-lock.json

    You're ready to transpile! Type

    npm run build

    and press Enter. Babel will do its thing, create the lib output directory for you, and write the ES5 version of index.js to it. The result in lib/index.js will look something like Listing 1-3.

    LISTING 1-3: ES2015 transpiling example output—index-transpiled-to-es5.js

    use strict; var obj = {   rex: /\d/,   checkArray: function checkArray(array) {     var _this = this;     return array.some(function (entry) {       return _this.rex.test(entry);     });   } }; console.log(obj.checkArray([no, digits, in, this, array])); // false console.log(obj.checkArray([this, array, has, 1, digit])); // true

    If you compare src/index.js (Listing 1-2) to lib/index.js (Listing 1-3), you'll see only a couple of changes (other than whitespace). First, Babel added a use strict; directive to the top of the transpiled file (recall that strict mode is a feature added in ES5 that modifies the behavior of a couple of things that were problematic for various reasons). This is Babel's default, but it can be turned off if you have code that relies on loose mode.

    The interesting thing, though, is how it rewrote the arrow function. It created a variable within checkArray called _this, set its value to this, and then used a traditional function as the some callback; within the function, it used _this instead of this. That fits with my earlier description of arrow functions—that they close over this just like closing over a variable. Babel just made that happen in a way an ES5 environment can understand.

    This is obviously just a very small example, but it illustrates the point and gives you a taste of one tool you might use if you need to do this in your projects. Babel can be integrated into your build system, whether you use Gulp,²⁸ Grunt,²⁹ Webpack,³⁰ Browserify,³¹ Rollup,³² or just about any other; the installation page at https://babeljs.io/docs/setup/#installation has instructions for all the major ones.

    REVIEW

    JavaScript has changed enormously over the past several years, particularly 2015 onward. It will continue to change in the future. But there's no need to be concerned about getting overloaded with all the new features; JavaScript will always be backward-compatible, so there's no need to adopt a new feature until/unless you're ready to do so and have need of it.

    The new features run the gamut from small tweaks like allowing a trailing comma in a function call arguments list and new convenience methods in the standard library, to major improvements like declarative class syntax (Chapter 4), async functions (Chapter 9), and modules (Chapter 13).

    The people ultimately in charge of moving JavaScript forward are the members of TC39 (Technical Committee 39 of Ecma International), which is made up of JavaScript developers, programming language researchers, library and large website authors, representatives from major JavaScript engines, and other stakeholders in JavaScript's success and future. But anyone can get involved.

    The process for new features is public and open. You can follow progress and stay up to date via various GitHub repositories, published meeting notes, and the TC39 Discourse group. Also, I continue coverage where this book leaves off on the book's site at https://thenewtoys.dev.

    Most of the features covered in this book are supported by the JavaScript engines in cutting-edge browsers like Chrome, Firefox, Safari, and Edge, and by recent releases of engines in non-browser environments like Node.js, Electron, React Native, etc.

    Older environments like Internet Explorer can be supported via JavaScript-to-JavaScript compilation (aka transpilation), converting old-style code into new-style code, with tools (like Babel) that can be integrated into your build system. Some features (like the Proxy objects you'll learn about in Chapter 14) can't be fully supported in this way, but many can.

    Okay. The stage is set …

    On to the new toys!

    NOTES

    1   Formerly known as the European Computer Manufacturer's Association (ECMA), but now only the E in Ecma is capitalized in the organization's name.

    2http://www.ecma-international.org/memento/TC39.htm

    3https://tc39.es/ecma262/

    4https://www.electronjs.org/

    5https://reactnative.dev/

    6https://nodejs.org/

    7   The committee also cares about significant JavaScript code that isn’t directly related to the web, too.

    8https://tc39.es/ecma402/

    9https://ecma-international.org/memento/join.htm

    10https://github.com/tc39/ecma262

    11https://github.com/tc39/proposals

    12https://es.discourse.group/

    13https://tc39.es/process-document/

    14https://tc39.es/agreements/contributor/

    15https://github.com/tc39/test262

    16https://tc39.es/code-of-conduct/

    17 The Discourse instance largely replaces the informal discussion es-discuss mailing list. The list still exists, but many TC39 representatives recommend avoiding its use.

    18 At least not properly; it has an incomplete version of let and const.

    19https://github.com/es-shims/es5-shim

    20https://github.com/zloirock/core-js

    21https://github.com/es-shims/

    22https://github.com/google/traceur-compiler

    23http://babeljs.io/

    24http://typescriptlang.org/

    25https://facebook.github.io/jsx/

    26https://reactjs.org/

    27https://babeljs.io/docs/en/babel-preset-env#docsNav

    28https://gulpjs.com/

    29https://gruntjs.com/

    30https://webpack.js.org/

    31http://browserify.org/

    32https://rollupjs.org/

    2

    Block-Scoped Declarations: let and const

    WHAT'S IN THIS CHAPTER?

    An introduction to let and const

    Definition of block scope with examples

    Shadowing and hoisting: the Temporal Dead Zone

    Using const for variables that shouldn't change

    Creating global variables that aren't on the global object

    Using block scope in loops

    CODE DOWNLOADS FOR THIS CHAPTER

    You can download the code for this chapter at https://thenewtoys.dev/bookcode or https://www.wiley.com/go/javascript-newtoys.

    In this chapter you'll learn how the new let and const declarations work and what problems they solve. Throughout the chapter, you'll see some behaviors of var that have proved problematic, and you'll learn how let and const solve those problems. You'll see how let and const provide true block scope and prevent confusion caused by repeated declarations or by using a variable before you initialize it. You'll discover how block scope means you can use let to avoid the traditional closures in loop problem, and how const lets you create constants: variables whose values cannot change. You'll learn how let and const avoid creating even more properties on the already-overburdened global object. In short, you'll learn why let and const are the new var, and why var no longer has a place in modern JavaScript programming.

    AN INTRODUCTION TO LET AND CONST

    Like var, let declares variables:

    let x = 2; x += 40; console.log(x); // 42

    You can use let anywhere you can use var. Just as with var, you don't have to use an initializer with let; if you don't, the variable's value defaults to undefined:

    let a; console.log(a); // undefined

    That's about where the similarities between let and var end. As you'll learn throughout this chapter, other than those basic similarities, var and let behave quite differently. More on that later; let's look at const.

    const declares constants:

    const value = Math.random(); console.log(value < 0.5 ? Heads : Tails);

    Constants are just like variables, except their values can't change. Because of that, you do need to supply an initializer; constants have no default value. Other than creating constants instead of variables and requiring an initializer, const is just like let. It's also much more useful than you probably expect, as you'll see across the course of this chapter.

    TRUE BLOCK SCOPE

    var jumps out of blocks. If you declare a variable within a block using var, it's not just in scope within that block, but outside it as well:

    function jumpOut() {     var a = [1, 2, 3];     for (var i = 0; i < a.length; ++i) {         var value = a[i];         console.log(value);     }     console.log(Outside loop + value);  // Why can we use 'value' here? } jumpOut();

    The author of jumpOut probably didn't mean for value to be accessible outside the loop, but it is. (So is i.) Why is this a problem? There are a couple of reasons. First, variables should be scoped as narrowly as possible for maintainability reasons; they should only be around as long as you need them, and no longer. Second, any time the apparent intent of code and its actual effect differ, you're asking for bugs and maintenance issues.

    let and const solve this by having true block scope: they exist only within the block they're declared in. Here's an example with let:

    function stayContained() {     var a = [1, 2, 3];     for (var i = 0; i < a.length; ++i) {        

    let

    value = a[i];         console.log(value);     }     console.log(Outside loop + value); //

    ReferenceError: 'value' is not defined

    } stayContained();

    Now, value is scoped to the block in which it appears. It doesn't exist in the rest of the function. It's only around for as long as it's needed, and the apparent intent matches the actual effect.

    (In stayContained, I didn't change the other variables from var to let. That was just to highlight the fact that what mattered was changing the declaration of value. Naturally, you can change the others as well.)

    REPEATED DECLARATIONS ARE AN ERROR

    var is happy to let you repeat yourself. You can declare the same variable with var as many times as you like. For example:

    function redundantRepetition() {     var x = alpha;     console.log(x);     // …lots of code here…     var x = bravo;     console.log(x);     // …lots of code here…     return x; } redundantRepetition();

    That code is perfectly correct syntactically. The fact that it declares x more than once is completely ignored by the JavaScript engine; it creates a single x variable that's used throughout the function. As with the var in a block earlier, though, the apparent intent of the code and its actual effect are at odds with one another. Redeclaring a variable you've already declared is probably a mistake. In this case, it's likely that the original author of redundantRepetition didn't have the middle bit there and it was supposed to return alpha; but then someone else came along later and added a bit in the middle, not realizing x was already in use.

    Like many things, good programming practice (keeping functions short) and/or lint tools and/or a good IDE can help here, which is great, but now so can JavaScript itself— let and const make repeated declarations in the same scope an error:

    function redundantRepetition() {    

    let

    x = alpha;     console.log(x);     // …lots of code here…    

    let

    x = bravo;    //

    SyntaxError: Identifier 'x' has already been declared

        console.log(x);     // …lots of code here…     return x; } redundantRepetition();

    It's the best kind of error, too: a proactive one. The error is raised when the code is parsed; it doesn't wait until later when you call redundantRepetition before telling you about the problem.

    HOISTING AND THE TEMPORAL DEAD ZONE

    var declarations are famously hoisted. With var, you can use a variable before you declare it:

    function example() {     console.log(answer);     answer = 42;     console.log(answer);     var answer = 67; } example();

    When you run example, it outputs the following:

    undefined 42

    We've cheerfully used the variable before it was declared, but the var declaration was seemingly moved to the top of the function. And only the declaration moved, not the initializer attached to it (the = 67 part of var answer = 67).

    This happens because when entering the example function, the JavaScript engine scans through the function handling var declarations and creating the necessary variables before it starts running any of the step-by-step code; it hoists (lifts) the declarations to the top of the function. When it does that, it initializes the variables they declare with a default value of undefined. But again, the apparent intent of the code and its actual effect are out of sync, which probably means there's a bug here. It looks like that first line is trying to assign to an answer variable that is in a containing scope (perhaps even a global), but instead it uses a local. It also looks like the author intended that when answer was created, it would start out with the value 67.

    With let and const, you can't use a variable until its declaration is processed in the step-by-step execution of the code:

    function boringOldLinearTime() {     answer = 42;            //

    ReferenceError: 'answer' is not defined

        console.log(answer);    

    let

    answer; } boringOldLinearTime();

    Seemingly, the let declaration isn't hoisted up to the top of the function like a var declaration is. But that's a popular misconception: let and const are hoisted, too. They're just hoisted differently.

    Consider the observation earlier that the code may have been trying to assign to an answer in a containing scope. Let's look at that scenario:

    let answer;                // The outer 'answer' function hoisting() {     answer = 42;            // ReferenceError: 'answer' is not defined     console.log(answer);     let answer;            // The inner 'answer' } hoisting();

    If the inner answer doesn't exist until the let answer; statement at the end, then at the beginning of the function where answer = 42; is, shouldn't that line assign to the outer answer?

    It could have been designed that way, yes; but how confusing would that be? Using an identifier for one thing early in the scope but for something else later in the scope is asking for bugs.

    Instead, let and const use a concept called the Temporal Dead Zone (TDZ), a period of time within the code execution where the identifier can't be used at all, not even to refer to something in a containing scope. Just like with var, the JavaScript engine looks through the code for let and const declarations and processes them before starting the step-by-step execution of the code. But instead of making answer accessible and giving it the value undefined, the engine marks answer as not yet initialized:

    let answer;                // The outer 'answer' function notInitializedYet() {                             // Reserve 'answer' here     answer = 42;            // ReferenceError: 'answer' is not defined     console.log(answer);     let answer;            // The inner 'answer' } notInitializedYet();

    The TDZ starts when code execution enters the scope where the declaration appears and continues until the declaration is run (along with any initializer attached to it). In this example, the inner answer is reserved at the beginning of notInitializedYet (that's where the TDZ starts) and initialized where the declaration is (that's where the TDZ ends). So let and const are still hoisted, they're just hoisted differently than var is.

    It's important to understand that the TDZ is temporal (related to time), not spatial (related to space/location). It's not a section at the top of the scope where the identifier can't be used. It's a period of time during which the identifier can't be used. Run the code in Listing 2-1.

    LISTING 2-1: Example of the temporal nature of the TDZ—tdz-is-temporal.js

    function temporalExample() {     const f = () => {         console.log(value);     };     let value = 42;     f(); } temporalExample();

    If the TDZ were spatial, if it were a block of code at the top of temporalExample where value couldn't be used, that code wouldn't work. But the TDZ is temporal, and by the time f uses value, the declaration has been run, so there's no problem. If you swapped the last two lines of that function, moving the f(); line above the let value = 42; line, then it would fail because f would try to use value before it was initialized. (Try it!)

    The TDZ applies to blocks just as much as it applies to functions:

    function blockExample(str) {     let p = prefix;                // The outer 'p' declaration     if (str) {         p = p.toUpperCase();        // ReferenceError: 'p' is not defined         str = str.toUpperCase();         let p = str.indexOf(X);    // The inner 'p' declaration         if (p != -1) {             str = str.substring(0, p);         }     }     return p + str; }

    You can't use p in that first line inside the block, because even though it's declared in the function, there's a shadowing declaration inside the block taking ownership of the p identifier. So the identifier can only refer to the new inner p, and only after the let declaration has been run. This prevents confusion about which p the code is using.

    A NEW KIND OF GLOBAL

    When you use var at global scope, it creates a global variable. In ES5 and earlier, all global variables were also properties of the global object. But that changed with ES2015: now JavaScript has traditional globals created with var (which are also properties of the global object) and also new-style globals (which are not properties of the global object). let and const at global scope create this new kind of global.

    ACCESSING THE GLOBAL OBJECT

    You'll recall that there's one global object, and you can get access to it via this at global scope, or by using a global that the environment defines for it (if any) such as window or self on browsers or global on Node.js. (In some environments, such as browsers, it’s not really the global object, it’s a facade on the global object, but that’s close enough.)

    Here's

    Enjoying the preview?
    Page 1 of 1