Introduction

The JavaScript core language features are defined in a standard called ECMA-262. The language defined in this standard is called ECMAScript. What you know as JavaScript in browsers and Node.js is actually a superset of ECMAScript. Browsers and Node.js add more functionality through additional objects and methods, but the core of the language remains as defined in ECMAScript. The ongoing development of ECMA-262 is vital to the success of JavaScript as a whole, and this book covers the changes brought about by the most recent major update to the language: ECMAScript 6.

The Road to ECMAScript 6

In 2007, JavaScript was at a crossroads. The popularity of Ajax was ushering in a new age of dynamic web applications, while JavaScript hadn’t changed since the third edition of ECMA-262 was published in 1999. TC-39, the committee responsible for driving the ECMAScript development process, put together a large draft specification for ECMAScript 4. ECMAScript 4 was massive in scope, introducing changes both small and large to the language. Updated features included new syntax, modules, classes, classical inheritance, private object members, optional type annotations, and more.

The scope of the ECMAScript 4 changes caused a rift to form in TC-39, with some members feeling that the fourth edition was trying to accomplish too much. A group of leaders from Yahoo, Google, and Microsoft created an alternate proposal for the next version of ECMAScript that they initially called ECMAScript 3.1. The “3.1” was intended to show that this was an incremental change to the existing standard.

ECMAScript 3.1 introduced very few syntax changes, instead focusing on property attributes, native JSON support, and adding methods to already-existing objects. Although there was an early attempt to reconcile ECMAScript 3.1 and ECMAScript 4, this ultimately failed as the two camps had difficulty with the very different perspectives on how the language should grow.

In 2008, Brendan Eich, the creator of JavaScript, announced that TC-39 would focus its efforts on standardizing ECMAScript 3.1. They would table the major syntax and feature changes of ECMAScript 4 until after the next version of ECMAScript was standardized, and all members of the committee would work to bring the best pieces of ECMAScript 3.1 and 4 together after that point into an effort initially nicknamed ECMAScript Harmony.

ECMAScript 3.1 was eventually standardized as the fifth edition of ECMA-262, also described as ECMAScript 5. The committee never released an ECMAScript 4 standard to avoid confusion with the now-defunct effort of the same name. Work then began on ECMAScript Harmony, with ECMAScript 6 being the first standard released in this new “harmonious” spirit.

ECMAScript 6 reached feature complete status in 2015 and was formally dubbed “ECMAScript 2015.” (But this text still refers to it as ECMAScript 6, the name most familiar to developers.) The features vary widely from completely new objects and patterns to syntax changes to new methods on existing objects. The exciting thing about ECMAScript 6 is that all of its changes are geared toward solving problems that developers actually face.

About This Book

A good understanding of ECMAScript 6 features is key for all JavaScript developers going forward. The language features introduced in ECMAScript 6 represent the foundation upon which JavaScript applications will be built for the foreseeable future. That’s where this book comes in. My hope is that you’ll read this book to learn about ECMAScript 6 features so that you’ll be ready to start using them as soon as you need to.

Browser and Node.js Compatibility

Many JavaScript environments, such as web browsers and Node.js, are actively working on implementing ECMAScript 6. This book doesn’t attempt to address the inconsistencies between implementations and instead focuses on what the specification defines as the correct behavior. As such, it’s possible that your JavaScript environment may not conform to the behavior described in this book.

Who This Book is For

This book is intended as a guide for those who are already familiar with JavaScript and ECMAScript 5. While a deep understanding of the language isn’t necessary to use this book, it will help you understand the differences between ECMAScript 5 and 6. In particular, this book is aimed at intermediate-to-advanced JavaScript developers programming for a browser or Node.js environment who want to learn about the latest developments in the language.

This book is not for beginners who have never written JavaScript. You will need to have a good basic understanding of the language to make use of this book.

Overview

Each of this book’s thirteen chapters covers a different aspect of ECMAScript 6. Many chapters start by discussing problems that ECMAScript 6 changes were made to solve, to give you a broader context for those changes, and all chapters include code examples to help you learn new syntax and concepts.

Chapter 1: How Block Bindings Work talks about let and const , the block-level replacement for var .

Chapter 2: Strings and Regular Expressions covers additional functionality for string manipulation and inspection as well as the introduction of template strings.

Chapter 3: Functions in ECMAScript 6 discusses the various changes to functions. This includes the arrow function form, default parameters, rest parameters, and more.

Chapter 4: Expanded Object Functionality explains the changes to how objects are created, modified, and used. Topics include changes to object literal syntax, and new reflection methods.

Chapter 5: Destructuring for Easier Data Access introduces object and array destructuring, which allow you to decompose objects and arrays using a concise syntax.

Chapter 6: Symbols and Symbol Properties introduces the concept of symbols, a new way to define properties. Symbols are a new primitive type that can be used to obscure (but not hide) object properties and methods.

Chapter 7: Sets and Maps details the new collection types of Set , WeakSet , Map , and WeakMap . These types expand on the usefulness of arrays by adding semantics, de-duping, and memory management designed specifically for JavaScript.

Chapter 8: Iterators and Generators discusses the addition of iterators and generators to the language. These features allow you to work with collections of data in powerful ways that were not possible in previous versions of JavaScript.

Chapter 9: Introducing JavaScript Classes introduces the first formal concept of classes in JavaScript. Often a point of confusion for those coming from other languages, the addition of class syntax in JavaScript makes the language more approachable to others and more concise for enthusiasts.

Chapter 10: Improved Array Capabilities details the changes to native arrays and the interesting new ways they can be used in JavaScript.

Chapter 11: Promises and Asynchronous Programming introduces promises as a new part of the language. Promises were a grassroots effort that eventually took off and gained in popularity due to extensive library support. ECMAScript 6 formalizes promises and makes them available by default.

Chapter 12: Proxies and the Reflection API introduces the formalized reflection API for JavaScript and the new proxy object that allows you to intercept every operation performed on an object. Proxies give developers unprecedented control over objects and, as such, unlimited possibilities for defining new interaction patterns.

Chapter 13: Encapsulating Code with Modules details the official module format for JavaScript. The intent is that these modules can replace the numerous ad-hoc module definition formats that have appeared over the years.

Appendix A: Smaller ECMAScript 6 Changes covers other changes implemented in ECMAScript 6 that you’ll use less frequently or that didn’t quite fit into the broader major topics covered in each chapter.

Appendix B: Understanding ECMAScript 7 (2016) describes the two additions to the standard that were implemented for ECMAScript 7, which didn’t impact JavaScript nearly as much as ECMAScript 6.

Conventions Used

The following typographical conventions are used in this book:

Italics introduces new terms

Constant width indicates a piece of code or filename

Additionally, longer code examples are contained in constant width code blocks such as:

function doSomething () { // empty }

Within a code block, comments to the right of a console.log() statement indicate the output you’ll see in the browser or Node.js console when the code is executed, for example:

console . log ( "Hi" ); // "Hi"

If a line of code in a code block throws an error, this is also indicated to the right of the code:

doSomething (); // error!

Help and Support

You can file issues, suggest changes, and open pull requests against this book by visiting: https://github.com/nzakas/understandinges6

If you have questions as you read this book, please send a message to my mailing list: http://groups.google.com/group/zakasbooks.

Acknowledgments

Thanks to Jennifer Griffith-Delgado, Alison Law, and everyone at No Starch Press for their support and help with this book. Their understanding and patience as my productivity slowed to a crawl during my extended illness is something I will never forget.

I’m grateful for the watchful eye of Juriy Zaytsev as tech editor and to Dr. Axel Rauschmayer for his feedback and several conversations that helped to clarify some of the concepts discussed in this book.

Thanks to everyone who submitted fixes to the version of this book that is hosted on GitHub: ShMcK, Ronen Elster, Rick Waldron, blacktail, Paul Salaets, Lonniebiz, Igor Skuhar, jakub-g, David Chang, Kevin Sweeney, Kyle Simpson, Peter Bakondy, Philip Borisov, Shaun Hickson, Steven Foote, kavun, Dan Kielp, Darren Huskie, Jakub Narębski, Jamund Ferguson, Josh Lubaway, Marián Rusnák, Nikolas Poniros, Robin Pokorný, Roman Lo, Yang Su, alexyans, robertd, 404, Aaron Dandy, AbdulFattah Popoola, Adam Richeimer, Ahmad Ali, Aleksandar Djindjic, Arjunkumar, Ben Regenspan, Carlo Costantini, Dmitri Suvorov, Kyle Pollock, Mallory, Erik Sundahl, Ethan Brown, Eugene Zubarev, Francesco Pongiluppi, Jake Champion, Jeremy Caney, Joe Eames, Juriy Zaytsev, Kale Worsley, Kevin Lozandier, Lewis Ellis, Mohsen Azimi, Navaneeth Kesavan, Nick Bottomley, Niels Dequeker, Pahlevi Fikri Auliya, Prayag Verma, Raj Anand, Ross Gerbasi, Roy Ling, Sarbbottam Bandyopadhyay, and Shidhin.

Also, thanks to everyone who supported this book on Patreon: Casey Visco.

Block Bindings

Traditionally, the way variable declarations work has been one tricky part of programming in JavaScript. In most C-based languages, variables (or bindings) are created at the spot where the declaration occurs. In JavaScript, however, this is not the case. Where your variables are actually created depends on how you declare them, and ECMAScript 6 offers options to make controlling scope easier. This chapter demonstrates why classic var declarations can be confusing, introduces block-level bindings in ECMAScript 6, and then offers some best practices for using them.

Var Declarations and Hoisting

Variable declarations using var are treated as if they are at the top of the function (or global scope, if declared outside of a function) regardless of where the actual declaration occurs; this is called hoisting. For a demonstration of what hoisting does, consider the following function definition:

function getValue ( condition ) { if ( condition ) { var value = "blue" ; // other code return value ; } else { // value exists here with a value of undefined return null ; } // value exists here with a value of undefined }

If you are unfamiliar with JavaScript, then you might expect the variable value to only be created if condition evaluates to true. In fact, the variable value is created regardless. Behind the scenes, the JavaScript engine changes the getValue function to look like this:

function getValue ( condition ) { var value ; if ( condition ) { value = "blue" ; // other code return value ; } else { return null ; } }

The declaration of value is hoisted to the top, while the initialization remains in the same spot. That means the variable value is actually still accessible from within the else clause. If accessed from there, the variable would just have a value of undefined because it hasn’t been initialized.

It often takes new JavaScript developers some time to get used to declaration hoisting, and misunderstanding this unique behavior can end up causing bugs. For this reason, ECMAScript 6 introduces block level scoping options to make the controlling a variable’s lifecycle a little more powerful.

Block-Level Declarations

Block-level declarations are those that declare variables that are inaccessible outside of a given block scope. Block scopes, also called lexical scopes, are created:

Inside of a function Inside of a block (indicated by the { and } characters)

Block scoping is how many C-based languages work, and the introduction of block-level declarations in ECMAScript 6 is intended to bring that same flexibility (and uniformity) to JavaScript.

Let Declarations

The let declaration syntax is the same as the syntax for var . You can basically replace var with let to declare a variable, but limit the variable’s scope to only the current code block (there are a few other subtle differences discussed a bit later, as well). Since let declarations are not hoisted to the top of the enclosing block, you may want to always place let declarations first in the block, so that they are available to the entire block. Here’s an example:

function getValue ( condition ) { if ( condition ) { let value = "blue" ; // other code return value ; } else { // value doesn't exist here return null ; } // value doesn't exist here }

This version of the getValue function behaves much closer to how you’d expect it to in other C-based languages. Since the variable value is declared using let instead of var , the declaration isn’t hoisted to the top of the function definition, and the variable value is no longer accessible once execution flows out of the if block. If condition evaluates to false, then value is never declared or initialized.

No Redeclaration

If an identifier has already been defined in a scope, then using the identifier in a let declaration inside that scope causes an error to be thrown. For example:

var count = 30 ; // Syntax error let count = 40 ;

In this example, count is declared twice: once with var and once with let . Because let will not redefine an identifier that already exists in the same scope, the let declaration will throw an error. On the other hand, no error is thrown if a let declaration creates a new variable with the same name as a variable in its containing scope, as demonstrated in the following code:

var count = 30 ; // Does not throw an error if ( condition ) { let count = 40 ; // more code }

This let declaration doesn’t throw an error because it creates a new variable called count within the if statement, instead of creating count in the surrounding block. Inside the if block, this new variable shadows the global count , preventing access to it until execution leaves the block.

Constant Declarations

You can also define variables in ECMAScript 6 with the const declaration syntax. Variables declared using const are considered constants, meaning their values cannot be changed once set. For this reason, every const variable must be initialized on declaration, as shown in this example:

// Valid constant const maxItems = 30 ; // Syntax error: missing initialization const name ;

The maxItems variable is initialized, so its const declaration should work without a problem. The name variable, however, would cause a syntax error if you tried to run the program containing this code, because name is not initialized.

Constants vs Let Declarations

Constants, like let declarations, are block-level declarations. That means constants are no longer accessible once execution flows out of the block in which they were declared, and declarations are not hoisted, as demonstrated in this example:

if ( condition ) { const maxItems = 5 ; // more code } // maxItems isn't accessible here

In this code, the constant maxItems is declared within an if statement. Once the statement finishes executing, maxItems is not accessible outside of that block.

In another similarity to let , a const declaration throws an error when made with an identifier for an already-defined variable in the same scope. It doesn’t matter if that variable was declared using var (for global or function scope) or let (for block scope). For example, consider this code:

var message = "Hello!" ; let age = 25 ; // Each of these would throw an error. const message = "Goodbye!" ; const age = 30 ;

The two const declarations would be valid alone, but given the previous var and let declarations in this case, neither will work as intended.

Despite those similarities, there is one big difference between let and const to remember. Attempting to assign a const to a previously defined constant will throw an error, in both strict and non-strict modes:

const maxItems = 5 ; maxItems = 6 ; // throws error

Much like constants in other languages, the maxItems variable can’t be assigned a new value later on. However, unlike constants in other languages, the value a constant holds may be modified if it is an object.

Declaring Objects with Const

A const declaration prevents modification of the binding and not of the value itself. That means const declarations for objects do not prevent modification of those objects. For example:

const person = { name : "Nicholas" }; // works person . name = "Greg" ; // throws an error person = { name : "Greg" };

Here, the binding person is created with an initial value of an object with one property. It’s possible to change person.name without causing an error because this changes what person contains and doesn’t change the value that person is bound to. When this code attempts to assign a value to person (thus attempting to change the binding), an error will be thrown. This subtlety in how const works with objects is easy to misunderstand. Just remember: const prevents modification of the binding, not modification of the bound value.

The Temporal Dead Zone

A variable declared with either let or const cannot be accessed until after the declaration. Attempting to do so results in a reference error, even when using normally safe operations such as the typeof operation in this example:

if ( condition ) { console . log ( typeof value ); // ReferenceError! let value = "blue" ; }

Here, the variable value is defined and initialized using let , but that statement is never executed because the previous line throws an error. The issue is that value exists in what the JavaScript community has dubbed the temporal dead zone (TDZ). The TDZ is never named explicitly in the ECMAScript specification, but the term is often used to describe why let and const declarations are not accessible before their declaration. This section covers some subtleties of declaration placement that the TDZ causes, and although the examples shown all use let , note that the same information applies to const .

When a JavaScript engine looks through an upcoming block and finds a variable declaration, it either hoists the declaration to the top of the function or global scope (for var ) or places the declaration in the TDZ (for let and const ). Any attempt to access a variable in the TDZ results in a runtime error. That variable is only removed from the TDZ, and therefore safe to use, once execution flows to the variable declaration.

This is true anytime you attempt to use a variable declared with let or const before it’s been defined. As the previous example demonstrated, this even applies to the normally safe typeof operator. You can, however, use typeof on a variable outside of the block where that variable is declared, though it may not give the results you’re after. Consider this code:

console . log ( typeof value ); // "undefined" if ( condition ) { let value = "blue" ; }

The variable value isn’t in the TDZ when the typeof operation executes because it occurs outside of the block in which value is declared. That means there is no value binding, and typeof simply returns "undefined" .

The TDZ is just one unique aspect of block bindings. Another unique aspect has to do with their use inside of loops.

Block Binding in Loops

Perhaps one area where developers most want block level scoping of variables is within for loops, where the throwaway counter variable is meant to be used only inside the loop. For instance, it’s not uncommon to see code like this in JavaScript:

for ( var i = 0 ; i < 10 ; i ++ ) { process ( items [ i ]); } // i is still accessible here console . log ( i ); // 10

In other languages, where block level scoping is the default, this example should work as intended, and only the for loop should have access to the i variable. In JavaScript, however, the variable i is still accessible after the loop is completed because the var declaration gets hoisted. Using let instead, as in the following code, should give the intended behavior:

for ( let i = 0 ; i < 10 ; i ++ ) { process ( items [ i ]); } // i is not accessible here - throws an error console . log ( i );

In this example, the variable i only exists within the for loop. Once the loop is complete, the variable is no longer accessible elsewhere.

Functions in Loops

The characteristics of var have long made creating functions inside of loops problematic, because the loop variables are accessible from outside the scope of the loop. Consider the following code:

var funcs = []; for ( var i = 0 ; i < 10 ; i ++ ) { funcs . push ( function () { console . log ( i ); }); } funcs . forEach ( function ( func ) { func (); // outputs the number "10" ten times });

You might ordinarily expect this code to print the numbers 0 to 9, but it outputs the number 10 ten times in a row. That’s because i is shared across each iteration of the loop, meaning the functions created inside the loop all hold a reference to the same variable. The variable i has a value of 10 once the loop completes, and so when console.log(i) is called, that value prints each time.

To fix this problem, developers use immediately-invoked function expressions (IIFEs) inside of loops to force a new copy of the variable they want to iterate over to be created, as in this example:

var funcs = []; for ( var i = 0 ; i < 10 ; i ++ ) { funcs . push (( function ( value ) { return function () { console . log ( value ); } }( i ))); } funcs . forEach ( function ( func ) { func (); // outputs 0, then 1, then 2, up to 9 });

This version uses an IIFE inside of the loop. The i variable is passed to the IIFE, which creates its own copy and stores it as value . This is the value used by the function for that iteration, so calling each function returns the expected value as the loop counts up from 0 to 9. Fortunately, block-level binding with let and const in ECMAScript 6 can simplify this loop for you.

Let Declarations in Loops

A let declaration simplifies loops by effectively mimicking what the IIFE does in the previous example. On each iteration, the loop creates a new variable and initializes it to the value of the variable with the same name from the previous iteration. That means you can omit the IIFE altogether and get the results you expect, like this:

var funcs = []; for ( let i = 0 ; i < 10 ; i ++ ) { funcs . push ( function () { console . log ( i ); }); } funcs . forEach ( function ( func ) { func (); // outputs 0, then 1, then 2, up to 9 })

This loop works exactly like the loop that used var and an IIFE but is, arguably, cleaner. The let declaration creates a new variable i each time through the loop, so each function created inside the loop gets its own copy of i . Each copy of i has the value it was assigned at the beginning of the loop iteration in which it was created. The same is true for for-in and for-of loops, as shown here:

var funcs = [], object = { a : true , b : true , c : true }; for ( let key in object ) { funcs . push ( function () { console . log ( key ); }); } funcs . forEach ( function ( func ) { func (); // outputs "a", then "b", then "c" });

In this example, the for-in loop shows the same behavior as the for loop. Each time through the loop, a new key binding is created, and so each function has its own copy of the key variable. The result is that each function outputs a different value. If var were used to declare key , all functions would output "c" .

It’s important to understand that the behavior of let declarations in loops is a specially-defined behavior in the specification and is not necessarily related to the non-hoisting characteristics of let . In fact, early implementations of let did not have this behavior, as it was added later on in the process.

Constant Declarations in Loops

The ECMAScript 6 specification doesn’t explicitly disallow const declarations in loops; however, there are different behaviors based on the type of loop you’re using. For a normal for loop, you can use const in the initializer, but the loop will throw a warning if you attempt to change the value. For example:

var funcs = []; // throws an error after one iteration for ( const i = 0 ; i < 10 ; i ++ ) { funcs . push ( function () { console . log ( i ); }); }

In this code, the i variable is declared as a constant. The first iteration of the loop, where i is 0, executes successfully. An error is thrown when i++ executes because it’s attempting to modify a constant. As such, you can only use const to declare a variable in the loop initializer if you are not modifying that variable.

When used in a for-in or for-of loop, on the other hand, a const variable behaves the same as a let variable. So the following should not cause an error:

var funcs = [], object = { a : true , b : true , c : true }; // doesn't cause an error for ( const key in object ) { funcs . push ( function () { console . log ( key ); }); } funcs . forEach ( function ( func ) { func (); // outputs "a", then "b", then "c" });

This code functions almost exactly the same as the second example in the “Let Declarations in Loops” section. The only difference is that the value of key cannot be changed inside the loop. The for-in and for-of loops work with const because the loop initializer creates a new binding on each iteration through the loop rather than attempting to modify the value of an existing binding (as was the case with the previous example using for instead of for-in ).

Global Block Bindings

Another way in which let and const are different from var is in their global scope behavior. When var is used in the global scope, it creates a new global variable, which is a property on the global object ( window in browsers). That means you can accidentally overwrite an existing global using var , such as:

// in a browser var RegExp = "Hello!" ; console . log ( window . RegExp ); // "Hello!" var ncz = "Hi!" ; console . log ( window . ncz ); // "Hi!"

Even though the RegExp global is defined on window , it is not safe from being overwritten by a var declaration. This example declares a new global variable RegExp that overwrites the original. Similarly, ncz is defined as a global variable and immediately defined as a property on window . This is the way JavaScript has always worked.

If you instead use let or const in the global scope, a new binding is created in the global scope but no property is added to the global object. That also means you cannot overwrite a global variable using let or const , you can only shadow it. Here’s an example:

// in a browser let RegExp = "Hello!" ; console . log ( RegExp ); // "Hello!" console . log ( window . RegExp === RegExp ); // false const ncz = "Hi!" ; console . log ( ncz ); // "Hi!" console . log ( "ncz" in window ); // false

Here, a new let declaration for RegExp creates a binding that shadows the global RegExp . That means window.RegExp and RegExp are not the same, so there is no disruption to the global scope. Also, the const declaration for ncz creates a binding but does not create a property on the global object. This capability makes let and const a lot safer to use in the global scope when you don’t want to create properties on the global object.

You may still want to use var in the global scope if you have a code that should be available from the global object. This is most common in a browser when you want to access code across frames or windows.

Emerging Best Practices for Block Bindings

While ECMAScript 6 was in development, there was widespread belief you should use let by default instead of var for variable declarations. For many JavaScript developers, let behaves exactly the way they thought var should have behaved, and so the direct replacement makes logical sense. In this case, you would use const for variables that needed modification protection.

However, as more developers migrated to ECMAScript 6, an alternate approach gained popularity: use const by default and only use let when you know a variable’s value needs to change. The rationale is that most variables should not change their value after initialization because unexpected value changes are a source of bugs. This idea has a significant amount of traction and is worth exploring in your code as you adopt ECMAScript 6.

Summary

The let and const block bindings introduce lexical scoping to JavaScript. These declarations are not hoisted and only exist within the block in which they are declared. This offers behavior that is more like other languages and less likely to cause unintentional errors, as variables can now be declared exactly where they are needed. As a side effect, you cannot access variables before they are declared, even with safe operators such as typeof . Attempting to access a block binding before its declaration results in an error due to the binding’s presence in the temporal dead zone (TDZ).

In many cases, let and const behave in a manner similar to var ; however, this is not true for loops. For both let and const , for-in and for-of loops create a new binding with each iteration through the loop. That means functions created inside the loop body can access the loop bindings values as they are during the current iteration, rather than as they were after the loop’s final iteration (the behavior with var ). The same is true for let declarations in for loops, while attempting to use const declarations in a for loop may result in an error.

The current best practice for block bindings is to use const by default and only use let when you know a variable’s value needs to change. This ensures a basic level of immutability in code that can help prevent certain types of errors.

Strings and Regular Expressions

Strings are arguably one of the most important data types in programming. They’re in nearly every higher-level programming language, and being able to work with them effectively is fundamental for developers to create useful programs. By extension, regular expressions are important because of the extra power they give developers to wield on strings. With these facts in mind, the creators of ECMAScript 6 improved strings and regular expressions by adding new capabilities and long-missing functionality. This chapter gives a tour of both types of changes.

Better Unicode Support

Before ECMAScript 6, JavaScript strings revolved around 16-bit character encoding (UTF-16). Each 16-bit sequence is a code unit representing a character. All string properties and methods, like the length property and the charAt() method, were based on these 16-bit code units. Of course, 16 bits used to be enough to contain any character. That’s no longer true thanks to the expanded character set introduced by Unicode.

UTF-16 Code Points

Limiting character length to 16 bits wasn’t possible for Unicode’s stated goal of providing a globally unique identifier to every character in the world. These globally unique identifiers, called code points, are simply numbers starting at 0. Code points are what you may think of as character codes, where a number represents a character. A character encoding must encode code points into code units that are internally consistent. For UTF-16, code points can be made up of many code units.

The first 216 code points in UTF-16 are represented as single 16-bit code units. This range is called the Basic Multilingual Plane (BMP). Everything beyond that is considered to be in one of the supplementary planes, where the code points can no longer be represented in just 16-bits. UTF-16 solves this problem by introducing surrogate pairs in which a single code point is represented by two 16-bit code units. That means any single character in a string can be either one code unit for BMP characters, giving a total of 16 bits, or two units for supplementary plane characters, giving a total of 32 bits.

In ECMAScript 5, all string operations work on 16-bit code units, meaning that you can get unexpected results from UTF-16 encoded strings containing surrogate pairs, as in this example:

var text = "𠮷" ; console . log ( text . length ); // 2 console . log ( /^.$/ . test ( text )); // false console . log ( text . charAt ( 0 )); // "" console . log ( text . charAt ( 1 )); // "" console . log ( text . charCodeAt ( 0 )); // 55362 console . log ( text . charCodeAt ( 1 )); // 57271

The single Unicode character "𠮷" is represented using surrogate pairs, and as such, the JavaScript string operations above treat the string as having two 16-bit characters. That means:

The length of text is 2, when it should be 1.

of is 2, when it should be 1. A regular expression trying to match a single character fails because it thinks there are two characters.

The charAt() method is unable to return a valid character string, because neither set of 16 bits corresponds to a printable character.

The charCodeAt() method also just can’t identify the character properly. It returns the appropriate 16-bit number for each code unit, but that is the closest you could get to the real value of text in ECMAScript 5.

ECMAScript 6, on the other hand, enforces UTF-16 string encoding to address problems like these. Standardizing string operations based on this character encoding means that JavaScript can support functionality designed to work specifically with surrogate pairs. The rest of this section discusses a few key examples of that functionality.

The codePointAt() Method

One method ECMAScript 6 added to fully support UTF-16 is the codePointAt() method, which retrieves the Unicode code point that maps to a given position in a string. This method accepts the code unit position rather than the character position and returns an integer value, as these console.log() examples show:

var text = "𠮷a" ; console . log ( text . charCodeAt ( 0 )); // 55362 console . log ( text . charCodeAt ( 1 )); // 57271 console . log ( text . charCodeAt ( 2 )); // 97 console . log ( text . codePointAt ( 0 )); // 134071 console . log ( text . codePointAt ( 1 )); // 57271 console . log ( text . codePointAt ( 2 )); // 97

The codePointAt() method returns the same value as the charCodeAt() method unless it operates on non-BMP characters. The first character in text is non-BMP and is therefore comprised of two code units, meaning the length property is 3 rather than 2. The charCodeAt() method returns only the first code unit for position 0, but codePointAt() returns the full code point even though the code point spans multiple code units. Both methods return the same value for positions 1 (the second code unit of the first character) and 2 (the "a" character).

Calling the codePointAt() method on a character is the easiest way to determine if that character is represented by one or two code units. Here’s a function you could write to check:

function is32Bit ( c ) { return c . codePointAt ( 0 ) > 0xFFFF ; } console . log ( is32Bit ( "𠮷" )); // true console . log ( is32Bit ( "a" )); // false

The upper bound of 16-bit characters is represented in hexadecimal as FFFF , so any code point above that number must be represented by two code units, for a total of 32 bits.

The String.fromCodePoint() Method

When ECMAScript provides a way to do something, it also tends to provide a way to do the reverse. You can use codePointAt() to retrieve the code point for a character in a string, while String.fromCodePoint() produces a single-character string from a given code point. For example:

console . log ( String . fromCodePoint ( 134071 )); // "𠮷"

Think of String.fromCodePoint() as a more complete version of the String.fromCharCode() method. Both give the same result for all characters in the BMP. There’s only a difference when you pass code points for characters outside of the BMP.

The normalize() Method

Another interesting aspect of Unicode is that different characters may be considered equivalent for the purpose of sorting or other comparison-based operations. There are two ways to define these relationships. First, canonical equivalence means that two sequences of code points are considered interchangeable in all respects. For example, a combination of two characters can be canonically equivalent to one character. The second relationship is compatibility. Two compatible sequences of code points look different but can be used interchangeably in certain situations.

Due to these relationships, two strings representing fundamentally the same text can contain different code point sequences. For example, the character “æ” and the two-character string “ae” may be used interchangeably but are strictly not equivalent unless normalized in some way.

ECMAScript 6 supports Unicode normalization forms by giving strings a normalize() method. This method optionally accepts a single string parameter indicating one of the following Unicode normalization forms to apply:

Normalization Form Canonical Composition ( "NFC" ), which is the default

), which is the default Normalization Form Canonical Decomposition ( "NFD" )

) Normalization Form Compatibility Composition ( "NFKC" )

) Normalization Form Compatibility Decomposition ( "NFKD" )

It’s beyond the scope of this book to explain the differences between these four forms. Just keep in mind that when comparing strings, both strings must be normalized to the same form. For example:

var normalized = values . map ( function ( text ) { return text . normalize (); }); normalized . sort ( function ( first , second ) { if ( first < second ) { return - 1 ; } else if ( first === second ) { return 0 ; } else { return 1 ; } });

This code converts the strings in the values array into a normalized form so that the array can be sorted appropriately. You can also sort the original array by calling normalize() as part of the comparator, as follows:

values . sort ( function ( first , second ) { var firstNormalized = first . normalize (), secondNormalized = second . normalize (); if ( firstNormalized < secondNormalized ) { return - 1 ; } else if ( firstNormalized === secondNormalized ) { return 0 ; } else { return 1 ; } });

Once again, the most important thing to note about this code is that both first and second are normalized in the same way. These examples have used the default, NFC, but you can just as easily specify one of the others, like this:

values . sort ( function ( first , second ) { var firstNormalized = first . normalize ( "NFD" ), secondNormalized = second . normalize ( "NFD" ); if ( firstNormalized < secondNormalized ) { return - 1 ; } else if ( firstNormalized === secondNormalized ) { return 0 ; } else { return 1 ; } });

If you’ve never worried about Unicode normalization before, then you probably won’t have much use for this method now. But if you ever work on an internationalized application, you’ll definitely find the normalize() method helpful.

Methods aren’t the only improvements that ECMAScript 6 provides for working with Unicode strings, though. The standard also adds two useful syntax elements.

The Regular Expression u Flag

You can accomplish many common string operations through regular expressions. But remember, regular expressions assume 16-bit code units, where each represents a single character. To address this problem, ECMAScript 6 defines a u flag for regular expressions, which stands for Unicode.

The u Flag in Action

When a regular expression has the u flag set, it switches modes to work on characters, not code units. That means the regular expression should no longer get confused about surrogate pairs in strings and should behave as expected. For example, consider this code:

var text = "𠮷" ; console . log ( text . length ); // 2 console . log ( /^.$/ . test ( text )); // false console . log ( /^.$/u . test ( text )); // true

The regular expression /^.$/ matches any input string with a single character. When used without the u flag, this regular expression matches on code units, and so the Japanese character (which is represented by two code units) doesn’t match the regular expression. When used with the u flag, the regular expression compares characters instead of code units and so the Japanese character matches.

Counting Code Points

Unfortunately, ECMAScript 6 doesn’t add a method to determine how many code points a string has, but with the u flag, you can use regular expressions to figure it out as follows:

function codePointLength ( text ) { var result = text . match ( /[\s\S]/gu ); return result ? result . length : 0 ; } console . log ( codePointLength ( "abc" )); // 3 console . log ( codePointLength ( "𠮷bc" )); // 3

This example calls match() to check text for both whitespace and non-whitespace characters (using [\s\S] to ensure the pattern matches newlines), using a regular expression that is applied globally with Unicode enabled. The result contains an array of matches when there’s at least one match, so the array length is the number of code points in the string. In Unicode, the strings "abc" and "𠮷bc" both have three characters, so the array length is three.

Although this approach works, it’s not very fast, especially when applied to long strings. You can use a string iterator (discussed in Chapter 8) as well. In general, try to minimize counting code points whenever possible.

Determining Support for the u Flag

Since the u flag is a syntax change, attempting to use it in JavaScript engines that aren’t compatible with ECMAScript 6 throws a syntax error. The safest way to determine if the u flag is supported is with a function, like this one:

function hasRegExpU () { try { var pattern = new RegExp ( "." , "u" ); return true ; } catch ( ex ) { return false ; } }

This function uses the RegExp constructor to pass in the u flag as an argument. This syntax is valid even in older JavaScript engines, but the constructor will throw an error if u isn’t supported.

If your code still needs to work in older JavaScript engines, always use the RegExp constructor when using the u flag. This will prevent syntax errors and allow you to optionally detect and use the u flag without aborting execution.

Other String Changes

JavaScript strings have always lagged behind similar features of other languages. It was only in ECMAScript 5 that strings finally gained a trim() method, for example, and ECMAScript 6 continues extending JavaScript’s capacity to parse strings with new functionality.

Methods for Identifying Substrings

Developers have used the indexOf() method to identify strings inside other strings since JavaScript was first introduced. ECMAScript 6 includes the following three methods, which are designed to do just that:

The includes() method returns true if the given text is found anywhere within the string. It returns false if not.

method returns true if the given text is found anywhere within the string. It returns false if not. The startsWith() method returns true if the given text is found at the beginning of the string. It returns false if not.

method returns true if the given text is found at the beginning of the string. It returns false if not. The endsWith() method returns true if the given text is found at the end of the string. It returns false if not.

Each methods accept two arguments: the text to search for and an optional index. When the second argument is provided, includes() and startsWith() start the match from that index while endsWith() starts the match from the second argument; when the second argument is omitted, includes() and startsWith() search from the beginning of the string, while endsWith() starts from the end. In effect, the second argument minimizes the amount of the string being searched. Here are some examples showing these three methods in action:

var msg = "Hello world!" ; console . log ( msg . startsWith ( "Hello" )); // true console . log ( msg . endsWith ( "!" )); // true console . log ( msg . includes ( "o" )); // true console . log ( msg . startsWith ( "o" )); // false console . log ( msg . endsWith ( "world!" )); // true console . log ( msg . includes ( "x" )); // false console . log ( msg . startsWith ( "o" , 4 )); // true console . log ( msg . endsWith ( "o" , 8 )); // true console . log ( msg . includes ( "o" , 8 )); // false

The first six calls don’t include a second parameter, so they’ll search the whole string if needed. The last three calls only check part of the string. The call to msg.startsWith("o", 4) starts the match by looking at index 4 of the msg string, which is the “o” in “Hello”. The call to msg.endsWith("o", 8) starts the search from index 0 and searches up to index 7, which is the “o” in “world”. The call to msg.includes("o", 8) starts the match from index 8, which is the “r” in “world”.

While these three methods make identifying the existence of substrings easier, each only returns a boolean value. If you need to find the actual position of one string within another, use the indexOf() or lastIndexOf() methods.

The startsWith() , endsWith() , and includes() methods will throw an error if you pass a regular expression instead of a string. This stands in contrast to indexOf() and lastIndexOf() , which both convert a regular expression argument into a string and then search for that string.

The repeat() Method

ECMAScript 6 also adds a repeat() method to strings, which accepts the number of times to repeat the string as an argument. It returns a new string containing the original string repeated the specified number of times. For example:

console . log ( "x" . repeat ( 3 )); // "xxx" console . log ( "hello" . repeat ( 2 )); // "hellohello" console . log ( "abc" . repeat ( 4 )); // "abcabcabcabc"

This method is a convenience function above all else, and it can be especially useful when manipulating text. It’s particularly useful in code formatting utilities that need to create indentation levels, like this:

// indent using a specified number of spaces var indent = " " . repeat ( 4 ), indentLevel = 0 ; // whenever you increase the indent var newIndent = indent . repeat ( ++ indentLevel );

The first repeat() call creates a string of four spaces, and the indentLevel variable keeps track of the indent level. Then, you can just call repeat() with an incremented indentLevel to change the number of spaces.

ECMAScript 6 also makes some useful changes to regular expression functionality that don’t fit into a particular category. The next section highlights a few.

Other Regular Expression Changes

Regular expressions are an important part of working with strings in JavaScript, and like many parts of the language, they haven’t changed much in recent versions. ECMAScript 6, however, makes several improvements to regular expressions to go along with the updates to strings.

The Regular Expression y Flag

ECMAScript 6 standardized the y flag after it was implemented in Firefox as a proprietary extension to regular expressions. The y flag affects a regular expression search’s sticky property, and it tells the search to start matching characters in a string at the position specified by the regular expression’s lastIndex property. If there is no match at that location, then the regular expression stops matching. To see how this works, consider the following code:

var text = "hello1 hello2 hello3" , pattern = /hello\d\s?/ , result = pattern . exec ( text ), globalPattern = /hello\d\s?/g , globalResult = globalPattern . exec ( text ), stickyPattern = /hello\d\s?/y , stickyResult = stickyPattern . exec ( text ); console . log ( result [ 0 ]); // "hello1 " console . log ( globalResult [ 0 ]); // "hello1 " console . log ( stickyResult [ 0 ]); // "hello1 " pattern . lastIndex = 1 ; globalPattern . lastIndex = 1 ; stickyPattern . lastIndex = 1 ; result = pattern . exec ( text ); globalResult = globalPattern . exec ( text ); stickyResult = stickyPattern . exec ( text ); console . log ( result [ 0 ]); // "hello1 " console . log ( globalResult [ 0 ]); // "hello2 " console . log ( stickyResult [ 0 ]); // Error! stickyResult is null

This example has three regular expressions. The expression in pattern has no flags, the one in globalPattern uses the g flag, and the one in stickyPattern uses the y flag. In the first trio of console.log() calls, all three regular expressions should return "hello1 " with a space at the end.

After that, the lastIndex property is changed to 1 on all three patterns, meaning that the regular expression should start matching from the second character on all of them. The regular expression with no flags completely ignores the change to lastIndex and still matches "hello1 " without incident. The regular expression with the g flag goes on to match "hello2 " because it is searching forward from the second character of the string ( "e" ). The sticky regular expression doesn’t match anything beginning at the second character so stickyResult is null .

The sticky flag saves the index of the next character after the last match in lastIndex whenever an operation is performed. If an operation results in no match, then lastIndex is set back to 0. The global flag behaves the same way, as demonstrated here:

var text = "hello1 hello2 hello3" , pattern = /hello\d\s?/ , result = pattern . exec ( text ), globalPattern = /hello\d\s?/g , globalResult = globalPattern . exec ( text ), stickyPattern = /hello\d\s?/y , stickyResult = stickyPattern . exec ( text ); console . log ( result [ 0 ]); // "hello1 " console . log ( globalResult [ 0 ]); // "hello1 " console . log ( stickyResult [ 0 ]); // "hello1 " console . log ( pattern . lastIndex ); // 0 console . log ( globalPattern . lastIndex ); // 7 console . log ( stickyPattern . lastIndex ); // 7 result = pattern . exec ( text ); globalResult = globalPattern . exec ( text ); stickyResult = stickyPattern . exec ( text ); console . log ( result [ 0 ]); // "hello1 " console . log ( globalResult [ 0 ]); // "hello2 " console . log ( stickyResult [ 0 ]); // "hello2 " console . log ( pattern . lastIndex ); // 0 console . log ( globalPattern . lastIndex ); // 14 console . log ( stickyPattern . lastIndex ); // 14

The value of lastIndex changes to 7 after the first call to exec() and to 14 after the second call, for both the stickyPattern and globalPattern variables.

There are two more subtle details about the sticky flag to keep in mind:

The lastIndex property is only honored when calling methods that exist on the regular expression object, like the exec() and test() methods. Passing the regular expression to a string method, such as match() , will not result in the sticky behavior. When using the ^ character to match the start of a string, sticky regular expressions only match from the start of the string (or the start of the line in multiline mode). While lastIndex is 0, the ^ makes a sticky regular expression no different from a non-sticky one. If lastIndex doesn’t correspond to the beginning of the string in single-line mode or the beginning of a line in multiline mode, the sticky regular expression will never match.

As with other regular expression flags, you can detect the presence of y by using a property. In this case, you’d check the sticky property, as follows:

var pattern = /hello\d/y ; console . log ( pattern . sticky ); // true

The sticky property is set to true if the sticky flag is present, and the property is false if not. The sticky property is read-only based on the presence of the flag and cannot be changed in code.

Similar to the u flag, the y flag is a syntax change, so it will cause a syntax error in older JavaScript engines. You can use the following approach to detect support:

function hasRegExpY () { try { var pattern = new RegExp ( "." , "y" ); return true ; } catch ( ex ) { return false ; } }

Just like the u check, this returns false if it’s unable to create a regular expression with the y flag. In one final similarity to u , if you need to use y in code that runs in older JavaScript engines, be sure to use the RegExp constructor when defining those regular expressions to avoid a syntax error.

Duplicating Regular Expressions

In ECMAScript 5, you can duplicate regular expressions by passing them into the RegExp constructor like this:

var re1 = /ab/i , re2 = new RegExp ( re1 );

The re2 variable is just a copy of the re1 variable. But if you provide the second argument to the RegExp constructor, which specifies the flags for the regular expression, your code won’t work, as in this example:

var re1 = /ab/i , // throws an error in ES5, okay in ES6 re2 = new RegExp ( re1 , "g" );

If you execute this code in an ECMAScript 5 environment, you’ll get an error stating that the second argument cannot be used when the first argument is a regular expression. ECMAScript 6 changed this behavior such that the second argument is allowed and overrides any flags present on the first argument. For example:

var re1 = /ab/i , // throws an error in ES5, okay in ES6 re2 = new RegExp ( re1 , "g" ); console . log ( re1 . toString ()); // "/ab/i" console . log ( re2 . toString ()); // "/ab/g" console . log ( re1 . test ( "ab" )); // true console . log ( re2 . test ( "ab" )); // true console . log ( re1 . test ( "AB" )); // true console . log ( re2 . test ( "AB" )); // false

In this code, re1 has the case-insensitive i flag while re2 has only the global g flag. The RegExp constructor duplicated the pattern from re1 and substituted the g flag for the i flag. Without the second argument, re2 would have the same flags as re1 .

The flags Property

Along with adding a new flag and changing how you can work with flags, ECMAScript 6 added a property associated with them. In ECMAScript 5, you could get the text of a regular expression by using the source property, but to get the flag string, you’d have to parse the output of the toString() method as shown below:

function getFlags ( re ) { var text = re . toString (); return text . substring ( text . lastIndexOf ( "/" ) + 1 , text . length ); } // toString() is "/ab/g" var re = /ab/g ; console . log ( getFlags ( re )); // "g"

This converts a regular expression into a string and then returns the characters found after the last / . Those characters are the flags.

ECMAScript 6 makes fetching flags easier by adding a flags property to go along with the source property. Both properties are prototype accessor properties with only a getter assigned, making them read-only. The flags property makes inspecting regular expressions easier for both debugging and inheritance purposes.

A late addition to ECMAScript 6, the flags property returns the string representation of any flags applied to a regular expression. For example:

var re = /ab/g ; console . log ( re . source ); // "ab" console . log ( re . flags ); // "g"

This fetches all flags on re and prints them to the console with far fewer lines of code than the toString() technique can. Using source and flags together allows you to extract the pieces of the regular expression that you need without parsing the regular expression string directly.

The changes to strings and regular expressions that this chapter has covered so far are definitely powerful, but ECMAScript 6 improves your power over strings in a much bigger way. It brings a type of literal to the table that makes strings more flexible.

Template Literals

JavaScript’s strings have always had limited functionality compared to strings in other languages. For instance, until ECMAScript 6, strings lacked the methods covered so far in this chapter, and string concatenation is as simple as possible. To allow developers to solve more complex problems, ECMAScript 6’s template literals provide syntax for creating domain-specific languages (DSLs) for working with content in a safer way than the solutions available in ECMAScript 5 and earlier. (A DSL is a programming language designed for a specific, narrow purpose, as opposed to general-purpose languages like JavaScript.) The ECMAScript wiki offers the following description on the template literal strawman:

This scheme extends ECMAScript syntax with syntactic sugar to allow libraries to provide DSLs that easily produce, query, and manipulate content from other languages that are immune or resistant to injection attacks such as XSS, SQL Injection, etc.

In reality, though, template literals are ECMAScript 6’s answer to the following features that JavaScript lacked all the way through ECMAScript 5:

Multiline strings A formal concept of multiline strings.

A formal concept of multiline strings. Basic string formatting The ability to substitute parts of the string for values contained in variables.

The ability to substitute parts of the string for values contained in variables. HTML escaping The ability to transform a string such that it is safe to insert into HTML.

Rather than trying to add more functionality to JavaScript’s already-existing strings, template literals represent an entirely new approach to solving these problems.

Basic Syntax

At their simplest, template literals act like regular strings delimited by backticks ( ` ) instead of double or single quotes. For example, consider the following:

let message = `Hello world!` ; console . log ( message ); // "Hello world!" console . log ( typeof message ); // "string" console . log ( message . length ); // 12

This code demonstrates that the variable message contains a normal JavaScript string. The template literal syntax is used to create the string value, which is then assigned to the message variable.

If you want to use a backtick in your string, then just escape it with a backslash ( \ ), as in this version of the message variable:

let message = `\`Hello\` world!` ; console . log ( message ); // "`Hello` world!" console . log ( typeof message ); // "string" console . log ( message . length ); // 14

There’s no need to escape either double or single quotes inside of template literals.

Multiline Strings

JavaScript developers have wanted a way to create multiline strings since the first version of the language. But when using double or single quotes, strings must be completely contained on a single line.

Pre-ECMAScript 6 Workarounds

Thanks to a long-standing syntax bug, JavaScript does have a workaround. You can create multiline strings if there’s a backslash ( \ ) before a newline. Here’s an example:

var message = "Multiline \ string" ; console . log ( message ); // "Multiline string"

The message string has no newlines present when printed to the console because the backslash is treated as a continuation rather than a newline. In order to show a newline in output, you’d need to manually include it:

var message = "Multiline

\ string" ; console . log ( message ); // "Multiline // string"

This should print Multiline String on two separate lines in all major JavaScript engines, but the behavior is defined as a bug and many developers recommend avoiding it.

Other pre-ECMAScript 6 attempts to create multiline strings usually relied on arrays or string concatenation, such as:

var message = [ "Multiline " , "string" ]. join ( "

" ); var message = "Multiline

" + "string" ;

All of the ways developers worked around JavaScript’s lack of multiline strings left something to be desired.

Multiline Strings the Easy Way

ECMAScript 6’s template literals make multiline strings easy because there’s no special syntax. Just include a newline where you want, and it shows up in the result. For example:

let message = `Multiline string` ; console . log ( message ); // "Multiline // string" console . log ( message . length ); // 16

All whitespace inside the backticks is part of the string, so be careful with indentation. For example:

let message = `Multiline string` ; console . log ( message ); // "Multiline // string" console . log ( message . length ); // 31

In this code, all whitespace before the second line of the template literal is considered part of the string itself. If making the text line up with proper indentation is important to you, then consider leaving nothing on the first line of a multiline template literal and then indenting after that, as follows:

let html = ` <div> <h1>Title</h1> </div>` . trim ();

This code begins the template literal on the first line but doesn’t have any text until the second. The HTML tags are indented to look correct and then the trim() method is called to remove the initial empty line.

If you prefer, you can also use

in a template literal to indicate where a newline should be inserted: let message = `Multiline

string`; console.log(message); // "Multiline // string" console.log(message.length); // 16

Making Substitutions

At this point, template literals may look like fancier versions of normal JavaScript strings. The real difference between the two lies in template literal substitutions. Substitutions allow you to embed any valid JavaScript expression inside a template literal and output the result as part of the string.

Substitutions are delimited by an opening ${ and a closing } that can have any JavaScript expression inside. The simplest substitutions let you embed local variables directly into a resulting string, like this:

let name = "Nicholas" , message = `Hello, ${ name } .` ; console . log ( message ); // "Hello, Nicholas."

The substitution ${name} accesses the local variable name to insert name into the message string. The message variable then holds the result of the substitution immediately.

A template literal can access any variable accessible in the scope in which it is defined. Attempting to use an undeclared variable in a template literal throws an error in both strict and non-strict modes.

Since all substitutions are JavaScript expressions, you can substitute more than just simple variable names. You can easily embed calculations, function calls, and more. For example:

let count = 10 , price = 0.25 , message = ` ${ count } items cost $ ${ ( count * price ). toFixed ( 2 ) } .` ; console . log ( message ); // "10 items cost $2.50."

This code performs a calculation as part of the template literal. The variables count and price are multiplied together to get a result, and then formatted to two decimal places using .toFixed() . The dollar sign before the second substitution is output as-is because it’s not followed by an opening curly brace.

Template literals are also JavaScript expressions, which means you can place a template literal inside of another template literal, as in this example:

let name = "Nicholas" , message = `Hello, ${ `my name is ${ name } ` } .` ; console . log ( message ); // "Hello, my name is Nicholas."

This example nests a second template literal inside the first. After the first ${ , another template literal begins. The second ${ indicates the beginning of an embedded expression inside the inner template literal. That expression is the variable name , which is inserted into the result.

Tagged Templates

Now you’ve seen how template literals can create multiline strings and insert values into strings without concatenation. But the real power of template literals comes from tagged templates. A template tag performs a transformation on the template literal and returns the final string value. This tag is specified at the start of the template, just before the first ` character, as shown here:

let message = tag `Hello world` ;

In this example, tag is the template tag to apply to the `Hello world` template literal.

A tag is simply a function that is called with the processed template literal data. The tag receives data about the template literal as individual pieces and must combine the pieces to create the result. The first argument is an array containing the literal strings as interpreted by JavaScript. Each subsequent argument is the interpreted value of each substitution.

Tag functions are typically defined using rest arguments as follows, to make dealing with the data easier:

function tag ( literals , ... substitutions ) { // return a string }

To better understand what gets passed to tags, consider the following:

let count = 10 , price = 0.25 , message = passthru ` ${ count } items cost $ ${ ( count * price ). toFixed ( 2 ) } .` ;

If you had a function called passthru() , that function would receive three arguments. First, it would get a literals array, containing the following elements:

The empty string before the first substitution ( "" )

) The string after the first substitution and before the second ( " items cost $" )

) The string after the second substitution ( "." )

The next argument would be 10 , which is the interpreted value for the count variable. This becomes the first element in a substitutions array. The final argument would be "2.50" , which is the interpreted value for (count * price).toFixed(2) and the second element in the substitutions array.

Note that the first item in literals is an empty string. This ensures that literals[0] is always the start of the string, just like literals[literals.length - 1] is always the end of the string. There is always one fewer substitution than literal, which means the expression substitutions.length === literals.length - 1 is always true.

Using this pattern, the literals and substitutions arrays can be interwoven to create a resulting string. The first item in literals comes first, the first item in substitutions is next, and so on, until the string is complete. As an example, you can mimic the default behavior of a template literal by alternating values from these two arrays:

function passthru ( literals , ... substitutions ) { let result = "" ; // run the loop only for the substitution count for ( let i = 0 ; i < substitutions . length ; i ++ ) { result += literals [ i ]; result += substitutions [ i ]; } // add the last literal result += literals [ literals . length - 1 ]; return result ; } let count = 10 , price = 0.25 , message = passthru ` ${ count } items cost $ ${ ( count * price ). toFixed ( 2 ) } .` ; console . log ( message ); // "10 items cost $2.50."

This example defines a passthru tag that performs the same transformation as the default template literal behavior. The only trick is to use substitutions.length for the loop rather than literals.length to avoid accidentally going past the end of the substitutions array. This works because the relationship between literals and substitutions is well-defined in ECMAScript 6.

The values contained in substitutions are not necessarily strings. If an expression evaluates to a number, as in the previous example, then the numeric value is passed in. Determining how such values should output in the result is part of the tag’s job.

Using Raw Values in Template Literals

Template tags also have access to raw string information, which primarily means access to character escapes before they are transformed into their character equivalents. The simplest way to work with raw string values is to use the built-in String.raw() tag. For example:

let message1 = `Multiline \ nstring` , message2 = String . raw `Multiline \ nstring` ; console . log ( message1 ); // "Multiline // string" console . log ( message2 ); // "Multiline

string"

In this code, the

in message1 is interpreted as a newline while the

in message2 is returned in its raw form of "\

" (the slash and n characters). Retrieving the raw string information like this allows for more complex processing when necessary.

The raw string information is also passed into template tags. The first argument in a tag function is an array with an extra property called raw . The raw property is an array containing the raw equivalent of each literal value. For example, the value in literals[0] always has an equivalent literals.raw[0] that contains the raw string information. Knowing that, you can mimic String.raw() using the following code:

function raw ( literals , ... substitutions ) { let result = "" ; // run the loop only for the substitution count for ( let i = 0 ; i < substitutions . length ; i ++ ) { result += literals . raw [ i ]; // use raw values instead result += substitutions [ i ]; } // add the last literal result += literals . raw [ literals . length - 1 ]; return result ; } let message = raw `Multiline \ nstring` ; console . log ( message ); // "Multiline

string" console . log ( message . length ); // 17

This uses literals.raw instead of literals to output the string result. That means any character escapes, including Unicode code point escapes, should be returned in their raw form. Raw strings are helpful when you want to output a string containing code in which you’ll need to include the character escaping (for instance, if you want to generate documentation about some code, you may want to output the actual code as it appears).

Summary

Full Unicode support allows JavaScript to deal with UTF-16 characters in logical ways. The ability to transfer between code point and character via codePointAt() and String.fromCodePoint() is an important step for string manipulation. The addition of the regular expression u flag makes it possible to operate on code points instead of 16-bit characters, and the normalize() method allows for more appropriate string comparisons.

ECMAScript 6 also added new methods for working with strings, allowing you to more easily identify a substring regardless of its position in the parent string. More functionality was added to regular expressions, too.

Template literals are an important addition to ECMAScript 6 that allows you to create domain-specific languages (DSLs) to make creating strings easier. The ability to embed variables directly into template literals means that developers have a safer tool than string concatenation for composing long strings with variables.

Built-in support for multiline strings also makes template literals a useful upgrade over normal JavaScript strings, which have never had this ability. Despite allowing newlines directly inside the template literal, you can still use

and other character escape sequences.

Template tags are the most important part of this feature for creating DSLs. Tags are functions that receive the pieces of the template literal as arguments. You can then use that data to return an appropriate string value. The data provided includes literals, their raw equivalents, and any substitution values. These pieces of information can then be used to determine the correct output for the tag.

Functions

Functions are an important part of any programming language, and prior to ECMAScript 6, JavaScript functions hadn’t changed much since the language was created. This left a backlog of problems and nuanced behavior that made making mistakes easy and often required more code just to achieve very basic behaviors.

ECMAScript 6 functions make a big leap forward, taking into account years of complaints and requests from JavaScript developers. The result is a number of incremental improvements on top of ECMAScript 5 functions that make programming in JavaScript less error-prone and more powerful.

Functions with Default Parameter Values

Functions in JavaScript are unique in that they allow any number of parameters to be passed, regardless of the number of parameters declared in the function definition. This allows you to define functions that can handle different numbers of parameters, often by just filling in default values when parameters aren’t provided. This section covers how default parameters work both in and prior to ECMAScript 6, along with some important information on the arguments object, using expressions as parameters, and another TDZ.

Simulating Default Parameter Values in ECMAScript 5

In ECMAScript 5 and earlier, you would likely use the following pattern to create a function with default parameters values:

function makeRequest ( url , timeout , callback ) { timeout = timeout || 2000 ; callback = callback || function () {}; // the rest of the function }

In this example, both timeout and callback are actually optional because they are given a default value if a parameter isn’t provided. The logical OR operator ( || ) always returns the second operand when the first is falsy. Since named function parameters that are not explicitly provided are set to undefined , the logical OR operator is frequently used to provide default values for missing parameters. There is a flaw with this approach, however, in that a valid value for timeout might actually be 0 , but this would replace it with 2000 because 0 is falsy.

In that case, a safer alternative is to check the type of the argument using typeof , as in this example:

function makeRequest ( url , timeout , callback ) { timeout = ( typeof timeout !== "undefined" ) ? timeout : 2000 ; callback = ( typeof callback !== "undefined" ) ? callback : function () {}; // the rest of the function }

While this approach is safer, it still requires a lot of extra code for a very basic operation. Popular JavaScript libraries are filled with similar patterns, as this represents a common pattern.

Default Parameter Values in ECMAScript 6

ECMAScript 6 makes it easier to provide default values for parameters by providing initializations that are used when the parameter isn’t formally passed. For example:

function makeRequest ( url , timeout = 2000 , callback = function () {}) { // the rest of the function }

This function only expects the first parameter to always be passed. The other two parameters have default values, which makes the body of the function much smaller because you don’t need to add any code to check for a missing value.

When makeRequest() is called with all three parameters, the defaults are not used. For example:

// uses default timeout and callback makeRequest ( "/foo" ); // uses default callback makeRequest ( "/foo" , 500 ); // doesn't use defaults makeRequest ( "/foo" , 500 , function ( body ) { doSomething ( body ); });

ECMAScript 6 considers url to be required, which is why "/foo" is passed in all three calls to makeRequest() . The two parameters with a default value are considered optional.

It’s possible to specify default values for any arguments, including those that appear before arguments without default values in the function declaration. For example, this is fine:

function makeRequest ( url , timeout = 2000 , callback ) { // the rest of the function }

In this case, the default value for timeout will only be used if there is no second argument passed in or if the second argument is explicitly passed in as undefined , as in this example:

// uses default timeout makeRequest ( "/foo" , undefined , function ( body ) { doSomething ( body ); }); // uses default timeout makeRequest ( "/foo" ); // doesn't use default timeout makeRequest ( "/foo" , null , function ( body ) { doSomething ( body ); });

In the case of default parameter values, a value of null is considered to be valid, meaning that in the third call to makeRequest() , the default value for timeout will not be used.

How Default Parameter Values Affect the arguments Object

Just keep in mind that the behavior of the arguments object is different when default parameter values are present. In ECMAScript 5 nonstrict mode, the arguments object reflects changes in the named parameters of a function. Here’s some code that illustrates how this works:

function mixArgs ( first , second ) { console . log ( first === arguments [ 0 ]); console . log ( second === arguments [ 1 ]); first = "c" ; second = "d" ; console . log ( first === arguments [ 0 ]); console . log ( second === arguments [ 1 ]); } mixArgs ( "a" , "b" );

This outputs:

true true true true

The arguments object is always updated in nonstrict mode to reflect changes in the named parameters. Thus, when first and second are assigned new values, arguments[0] and arguments[1] are updated accordingly, making all of the === comparisons resolve to true .

ECMAScript 5’s strict mode, however, eliminates this confusing aspect of the arguments object. In strict mode, the arguments object does not reflect changes to the named parameters. Here’s the mixArgs() function again, but in strict mode:

function mixArgs ( first , second ) { "use strict" ; console . log ( first === arguments [ 0 ]); console . log ( second === arguments [ 1 ]); first = "c" ; second = "d" console . log ( first === arguments [ 0 ]); console . log ( second === arguments [ 1 ]); } mixArgs ( "a" , "b" );

The call to mixArgs() outputs:

true true false false

This time, changing first and second doesn’t affect arguments , so the output behaves as you’d normally expect it to.

The arguments object in a function using ECMAScript 6 default parameter values, however, will always behave in the same manner as ECMAScript 5 strict mode, regardless of whether the function is explicitly running in strict mode. The presence of default parameter values triggers the arguments object to remain detached from the named parameters. This is a subtle but important detail because of how the arguments object may be used. Consider the following:

// not in strict mode function mixArgs ( first , second = "b" ) { console . log ( arguments . length ); console . log ( first === arguments [ 0 ]); console . log ( second === arguments [ 1 ]); first = "c" ; second = "d" console . log ( first === arguments [ 0 ]); console . log ( second === arguments [ 1 ]); } mixArgs ( "a" );

This outputs:

1 true false false false

In this example, arguments.length is 1 because only one argument was passed to mixArgs() . That also means arguments[1] is undefined , which is the expected behavior when only one argument is passed to a function. That means first is equal to arguments[0] as well. Changing first and second has no effect on arguments . This behavior occurs in both nonstrict and strict mode, so you can rely on arguments to always reflect the initial call state.

Default Parameter Expressions

Perhaps the most interesting feature of default parameter values is that the default value need not be a primitive value. You can, for example, execute a function to retrieve the default parameter value, like this:

function getValue () { return 5 ; } function add ( first , second = getValue ()) { return first + second ; } console . log ( add ( 1 , 1 )); // 2 console . log ( add ( 1 )); // 6

Here, if the last argument isn’t provided, the function getValue() is called to retrieve the correct default value. Keep in mind that getValue() is only called when add() is called without a second parameter, not when the function declaration is first parsed. That means if getValue() were written differently, it could potentially return a different value. For instance:

let value = 5 ; function getValue () { return value ++ ; } function add ( first , second = getValue ()) { return first + second ; } console . log ( add ( 1 , 1 )); // 2 console . log ( add ( 1 )); // 6 console . log ( add ( 1 )); // 7

In this example, value begins as five and increments each time getValue() is called. The first call to add(1) returns 6, while the second call to add(1) returns 7 because value was incremented. Because the default value for second is only evaluated when the function is called, changes to that value can be made at any time.

Be careful when using function calls as default parameter values. If you forget the parentheses, such as second = getValue in the last example, you are passing a reference to the function rather than the result of the function call.

This behavior introduces another interesting capability. You can use a previous parameter as the default for a later parameter. Here’s an example:

function add ( first , second = first ) { return first + second ; } console . log ( add ( 1 , 1 )); // 2 console . log ( add ( 1 )); // 2

In this code, the parameter second is given a default value of first , meaning that passing in just one argument leaves both arguments with the same value. So add(1, 1) returns 2 just as add(1) returns 2. Taking this a step further, you can pass first into a function to get the value for second as follows:

function getValue ( value ) { return value + 5 ; } function add ( first , second = getValue ( first )) { return first + second ; } console . log ( add ( 1 , 1 )); // 2 console . log ( add ( 1 )); // 7

This example sets second equal to the value returned by getValue(first) , so while add(1, 1) still returns 2, add(1) returns 7 (1 + 6).

The ability to reference parameters from default parameter assignments works only for previous arguments, so earlier arguments do not have access to later arguments. For example:

function add ( first = second , second ) { return first + second ; } console . log ( add ( 1 , 1 )); // 2 console . log ( add ( undefined , 1 )); // throws error

The call to add(undefined, 1) throws an error because second is defined after first and is therefore unavailable as a default value. To understand why that happens, it’s important to revisit temporal dead zones.

Default Parameter Value Temporal Dead Zone

Chapter 1 introduced the temporal dead zone (TDZ) as it relates to let and const , and default parameter values also have a TDZ where parameters cannot be accessed. Similar to a let declaration, each parameter creates a new identifier binding that can’t be referenced before initialization without throwing an error. Parameter initialization happens when the function is called, either by passing a value for the parameter or by using the default parameter value.

To explore the default parameter value TDZ, consider this example from “Default Parameter Expressions” again:

function getValue ( value ) { return value + 5 ; } function add ( first , second = getValue ( first )) { return first + second ; } console . log ( add ( 1 , 1 )); // 2 console . log ( add ( 1 )); // 7

The calls to add(1, 1) and add(1) effectively execute the following code to create the first and second parameter values:

// JavaScript representation of call to add(1, 1) let first = 1 ; let second = 1 ; // JavaScript representation of call to add(1) let first = 1 ; let second = getValue ( first );

When the function add() is first executed, the bindings first and second are added to a parameter-specific TDZ (similar to how let behaves). So while second can be initialized with the value of first because first is always initialized at that time, the reverse is not true. Now, consider this rewritten add() function:

function add ( first = second , second ) { return first + second ; } console . log ( add ( 1 , 1 )); // 2 console . log ( add ( undefined , 1 )); // throws error

The calls to add(1, 1) and add(undefined, 1) in this example now map to this code behind the scenes:

// JavaScript representation of call to add(1, 1) let first = 1 ; let second = 1 ; // JavaScript representation of call to add(undefined, 1) let first = second ; let second = 1 ;

In this example, the call to add(undefined, 1) throws an error because second hasn’t yet been initialized when first is initialized. At that point, second is in the TDZ and therefore any references to second throw an error. This mirrors the behavior of let bindings discussed in Chapter 1.

Function parameters have their own scope and their own TDZ that is separate from the function body scope. That means the default value of a parameter cannot access any variables declared inside the function body.

Working with Unnamed Parameters

So far, the examples in this chapter have only covered parameters that have been named in the function definition. However, JavaScript functions don’t limit the number of parameters that can be passed to the number of named parameters defined. You can always pass fewer or more parameters than formally specified. Default parameter values make it clear when a function can accept fewer parameters, and ECMAScript 6 sought to make the problem of passing more parameters than defined better as well.

Unnamed Parameters in ECMAScript 5

Early on, JavaScript provided the arguments object as a way to inspect all function parameters that are passed without necessarily defining each parameter individually. While inspecting arguments works fine in most cases, this object can be a little cumbersome to work with. For example, examine this code, which inspects the arguments object:

function pick ( object ) { let result = Object . create ( null ); // start at the second parameter for ( let i = 1 , len = arguments . length ; i < len ; i ++ ) { result [ arguments [ i ]] = object [ arguments [ i ]]; } return result ; } let book = { title : "Understanding ECMAScript 6" , author : "Nicholas C. Zakas" , year : 2015 }; let bookData = pick ( book , "author" , "year" ); console . log ( bookData . author ); // "Nicholas C. Zakas" console . log ( bookData . year ); // 2015

This function mimics the pick() method from the Underscore.js library, which returns a copy of a given object with some specified subset of the original object’s properties. This example defines only one argument and expects the first argument to be the object from which to copy properties. Every other argument passed is the name of a property that should be copied on the result.

There are a couple of things to notice about this pick() function. First, it’s not at all obvious that the function can handle more than one parameter. You could define several more parameters, but you would always fall short of indicating that this function can take any number of parameters. Second, because the first parameter is named and used directly, when you look for the properties to copy, you have to start in the arguments object at index 1 instead of index 0. Remembering to use the appropriate indices with arguments isn’t necessarily difficult, but it’s one more thing to keep track of.

ECMAScript 6 introduces rest parameters to help with these issues.

Rest Parameters

A rest parameter is indicated by three dots ( ... ) preceding a named parameter. That named parameter becomes an Array containing the rest of the parameters passed to the function, which is where the name “rest” parameters originates. For example, pick() can be rewritten using rest parameters, like this:

function pick ( object , ... keys ) { let result = Object . create ( null ); for ( let i = 0 , len = keys . length ; i < len ; i ++ ) { result [ keys [ i ]] = object [ keys [ i ]]; } return result ; }

In this version of the function, keys is a rest parameter that contains all parameters passed after object (unlike arguments , which contains all parameters including the first one). That means you can iterate over keys from beginning to end without worry. As a bonus, you can tell by looking at the function that it is capable of handling any number of parameters.

Rest parameters do not affect a function’s length property, which indicates the number of named parameters for the function. The value of length for pick() in this example is 1 because only object counts towards this value.

Rest Parameter Restrictions

There are two restrictions on rest parameters. The first restriction is that there can be only one rest parameter, and the rest parameter must be last. For example, this code won’t work:

// Syntax error: Can't have a named parameter after rest parameters function pick ( object , ... keys , last ) { let result = Object . create ( null ); for ( let i = 0 , len = keys . length ; i < len ; i ++ ) { result [ keys [ i ]] = object [ keys [ i ]]; } return result ; }

Here, the parameter last follows the rest parameter keys , which would cause a syntax error.

The second restriction is that rest parameters cannot be used in an object literal setter. That means this code would also cause a syntax error:

let object = { // Syntax error: Can't use rest param in setter set name (... value ) { // do something } };

This restriction exists because object literal setters are restricted to a single argument. Rest parameters are, by definition, an infinite number of arguments, so they’re not allowed in this context.

How Rest Parameters Affect the arguments Object

Rest parameters were designed to replace arguments in ECMAScript. Originally, ECMAScript 4 did away with arguments and added rest parameters to allow an unlimited number of arguments to be passed to functions. ECMAScript 4 never came into being, but this idea was kept around and reintroduced in ECMAScript 6, despite arguments not being removed from the language.

The arguments object works together with rest parameters by reflecting the arguments that were passed to the function when called, as illustrated in this program:

function checkArgs (... args ) { console . log ( args . length ); console . log ( arguments . length ); console . log ( args [ 0 ], arguments [ 0 ]); console . log ( args [ 1 ], arguments [ 1 ]); } checkArgs ( "a" , "b" );

The call to checkArgs() outputs:

2 2 a a b b

The arguments object always correctly reflects the parameters that were passed into a function regardless of rest parameter usage.

That’s all you really need to know about rest parameters to get started using them.

Increased Capabilities of the Function Constructor

The Function constructor is an infrequently used part of JavaScript that allows you to dynamically create a new function. The arguments to the constructor are the parameters for the function and the function body, all as strings. Here’s an example:

var add = new Function ( "first" , "second" , "return first + second" ); console . log ( add ( 1 , 1 )); // 2

ECMAScript 6 augments the capabilities of the Function constructor to allow default parameters and rest parameters. You need only add an equals sign and a value to the parameter names, as follows:

var add = new Function ( "first" , "second = first" , "return first + second" ); console . log ( add ( 1 , 1 )); // 2 console . log ( add ( 1 )); // 2

In this example, the parameter second is assigned the value of first when only one parameter is passed. The syntax is the same as for function declarations that don’t use Function .

For rest parameters, just add the ... before the last parameter, like this:

var pickFirst = new Function ( "...args" , "return args[0]" ); console . log ( pickFirst ( 1 , 2 )); // 1

This code creates a function that uses only a single rest parameter and returns the first argument that was passed in.

The addition of default and rest parameters ensures that Function has all of the same capabilities as the declarative form of creating functions.

The Spread Operator

Closely related to rest parameters is the spread operator. While rest parameters allow you to specify that multiple independent arguments should be combined into an array, the spread operator allows you to specify an array that should be split and have its items passed in as separate arguments to a function. Consider the Math.max() method, which accepts any number of arguments and returns the one with the highest value. Here’s a simple use case for this method:

let value1 = 25 , value2 = 50 ; console . log ( Math . max ( value1 , value2 )); // 50

When you’re dealing with just two values, as in this example, Math.max() is very easy to use. The two values are passed in, and the higher value is returned. But what if you’ve been tracking values in an array, and now you want to find the highest value? The Math.max() method doesn’t allow you to pass in an array, so in ECMAScript 5 and earlier, you’d be stuck either searching the array yourself or using apply() as follows:

let values = [ 25 , 50 , 75 , 100 ] console . log ( Math . max . apply ( Math , values )); // 100

This solution works, but using apply() in this manner is a bit confusing. It actually seems to obfuscate the true meaning of the code with additional syntax.

The ECMAScript 6 spread operator makes this case very simple. Instead of calling apply() , you can pass the array to Math.max() directly and prefix it with the same ... pattern used with rest parameters. The JavaScript engine then splits the array into individual arguments and passes them in, like this:

let values = [ 25 , 50 , 75 , 100 ] // equivalent to // console.log(Math.max(25, 50, 75, 100)); console . log ( Math . max (... values )); // 100

Now the call to Math.max() looks a bit more conventional and avoids the complexity of specifying a this -binding (the first argument to Math.max.apply() in the previous example) for a simple mathematical operation.

You can mix and match the spread operator with other arguments as well. Suppose you want the smallest number returned from Math.max() to be 0 (just in case negative numbers sneak into the array). You can pass that argument separately and still use the spread operator for the other arguments, as follows:

let values = [ - 25 , - 50 , - 75 , - 100 ] console . log ( Math . max (... values , 0 )); // 0

In this example, the last argument passed to Math.max() is 0 , which comes after the other arguments are passed in using the spread operator.

The spread operator for argument passing makes using arrays for function arguments much easier. You’ll likely find it to be a suitable replacement for the apply() method in most circumstances.

In addition to the uses you’ve seen for default and rest parameters so far, in ECMAScript 6, you can also apply both parameter types to JavaScript’s Function constructor.

ECMAScript 6’s name Property

Identifying functions can be challenging in JavaScript given the various ways a function can be defined. Additionally, the prevalence of anonymous function expressions makes debugging a bit more difficult, often resulting in stack traces that are hard to read and decipher. For these reasons, ECMAScript 6 adds the name property to all functions.

Choosing Appropriate Names

All functions in an ECMAScript 6 program will have an appropriate value for their name property. To see this in action, look at the following example, which shows a function and function expression, and prints the name properties for both:

function doSomething () { // ... } var doAnotherThing = function () { // ... }; console . log ( doSomething . name ); // "doSomething" console . log ( doAnotherThing . name ); // "doAnotherThing"

In this code, doSomething() has a name property equal to "doSomething" because it’s a function declaration. The anonymous function expression doAnotherThing() has a name of "doAnotherThing" because that’s the name of the variable to which it is assigned.

Special Cases of the name Property

While appropriate names for function declarations and function expressions are easy to find, ECMAScript 6 goes further to ensure that all functions have appropriate names. To illustrate this, consider the following program:

var doSomething = function doSomethingElse () { // ... }; var person = { get firstName () { return "Nicholas" }, sayName : function () { console . log ( this . name ); } } console . log ( doSomething . name ); // "doSomethingElse" console . log ( person . sayName . name ); // "sayName" var descriptor = Object . getOwnPropertyDescriptor ( person , "firstName" ); console . log ( descriptor . get . name ); // "get firstName"

In this example, doSomething.name is "doSomethingElse" because the function expression itself has a name, and that name takes priority over the variable to which the function was assigned. The name property of person.sayName() is "sayName" , as the value was interpreted from the object literal. Similarly, person.firstName is actually a getter function, so its name is "get firstName" to indicate this difference. Setter functions are prefixed with "set" as well. (Both getter and setter functions must be retrieved using Object.getOwnPropertyDescriptor() .)

There are a couple of other special cases for function names, too. Functions created using bind() will have their names prefixed with "bound" and functions created using the Function constructor have a name of "anonymous" , as in this example:

var doSomething = function () { // ... }; console . log ( doSomething . bind (). name ); // "bound doSomething" console . log (( new Function ()). name ); // "anonymous"

The name of a bound function will always be the name of the function being bound prefixed with the string "bound " , so the bound version of doSomething() is "bound doSomething" .

Keep in mind that the value of name for any function does not necessarily refer to a variable of the same name. The name property is meant to be informative, to help with debugging, so there’s no way to use the value of name to get a reference to the function.

Clarifying the Dual Purpose of Functions

In ECMAScript 5 and earlier, functions serve the dual purpose of being callable with or without new . When used with new , the this value inside a function is a new object and that new object is returned, as illustrated in this example:

function Person ( name ) { this . name = name ; } var person = new Person ( "Nicholas" ); var notAPerson = Person ( "Nicholas" ); console . log ( person ); // "[Object object]" console . log ( notAPerson ); // "undefined"

When creating notAPerson , calling Person() without new results in undefined (and sets a name property on the global object in nonstrict mode). The capitalization of Person is the only real indicator that the function is meant to be called using new , as is common in JavaScript programs. This confusion over the dual roles of functions led to some changes in ECMAScript 6.

JavaScript has two different internal-only methods for functions: [[Call]] and [[Construct]] . When a function is called without new , the [[Call]] method is executed, which executes the body of the function as it appears in the code. When a function is called with new , that’s when the [[Construct]] method is called. The [[Construct]] method is responsible for creating a new object, called the new target, and then executing the function body with this set to the new target. Functions that have a [[Construct]] method are called constructors.

Keep in mind that not all functions have [[Construct]] , and therefore not all functions can be called with new . Arrow functions, discussed in the “Arrow Functions” section, do not have a [[Construct]] method.

Determining How a Function was Called in ECMAScript 5

The most popular way to determine if a function was called with new (and hence, with constructor) in ECMAScript 5 is to use instanceof , for example:

function Person ( name ) { if ( this instanceof Person ) { this . name = name ; // using new } else { throw new Error ( "You must use new with Person." ) } } var person = new Person ( "Nicholas" ); var notAPerson = Person ( "Nicholas" ); // throws error

Here, the this value is checked to see if it’s an instance of the constructor, and if so, execution continues as normal. If this isn’t an instance of Person , then an error is thrown. This works because the [[Construct]] method creates a new instance of Person and assigns it to this . Unfortunately, this a