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, 1 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.” 2 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 3 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, 4 React Native, 5 and others), web servers and other kinds of servers (often using Node.js 6), 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 offor
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 ownthis
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 andawait
; 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
andReflect
- 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. 7 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 8 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. 9 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 10 with the up-to-date specification (browsable at https://tc39.es/ecma262/
) and a proposals repository 11 for proposals going through the TC39 process described in the next section. Some members are also active on the TC39 Discourse group. 12 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, 13 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 14 (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; 15 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. 16 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/
). 17 - 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, 18 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, 19 core-js, 20 es-shims, 21 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 22 and Babel 23 (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 24 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 25 (used in some JavaScript frameworks, notably React 26). 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 apackage.json
file: Typenpm 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 outpackage.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.) Typenpm 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 calledfsevents
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 topackage.json
. Openpackage.json
in your favorite editor. If there isn't a top-levelscripts
entry, create one (but current versions ofnpm
will have included one with atest
script that shows an error). Within thescripts
entry, add this setting:"build": "babel src -d lib"
Now your
package.json
should look something like Listing 1-1. (Yours may still have atest
entry inscripts
; 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 27 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"])); // falseconsole.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, 28 Grunt, 29 Webpack, 30 Browserify, 31 Rollup, 32 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!
Note
- Formerly known as the European Computer Manufacturer's Association (ECMA), but now only the E in Ecma is capitalized in the organization's name.
-
http://www.ecma-international.org/memento/TC39.htm
-
https://tc39.es/ecma262/
-
https://www.electronjs.org/
-
https://reactnative.dev/
-
https://nodejs.org/
- The committee also cares about significant JavaScript code that isn’t directly related to the web, too.
-
https://tc39.es/ecma402/
-
https://ecma-international.org/memento/join.htm
-
https://github.com/tc39/ecma262
-
https://github.com/tc39/proposals
-
https://es.discourse.group/
-
https://tc39.es/process-document/
-
https://tc39.es/agreements/contributor/
-
https://github.com/tc39/test262
-
https://tc39.es/code-of-conduct/
- The Discourse instance largely replaces the informal discussion es-discuss mailing list. The list still exists, but many TC39 representatives recommend avoiding its use.
- At least not properly; it has an incomplete version of let and const.
-
https://github.com/es-shims/es5-shim
-
https://github.com/zloirock/core-js
-
https://github.com/es-shims/
-
https://github.com/google/traceur-compiler
-
http://babeljs.io/
-
http://typescriptlang.org/
-
https://facebook.github.io/jsx/
-
https://reactjs.org/
-
https://babeljs.io/docs/en/babel-preset-env#docsNav
-
https://gulpjs.com/
-
https://gruntjs.com/
-
https://webpack.js.org/
-
http://browserify.org/
-
https://rollupjs.org/