12 mins read

Everything You Need to Know About ECMAScript 2019 (ES10)

Hey, JavaScript Lovers!

ECMAScript 2019, also known as ES2019 or ES10, introduced several new features and improvements to JavaScript. Here are some of the key updates from ES2019.

Some of the key features introduced in ES9 include:

  1. Array.prototype.flat() and Array.prototype.flatMap()
  2. Array.prototype.slice() with String and Symbol keys
  3. Object.fromEntries()
  4. String.prototype.trimStart() and String.prototype.trimEnd()
  5. Symbol.prototype.description
  6. Function.prototype.toString()
  7. Improved regular expressions

1. Array.prototype.flat() and Array.prototype.flatMap()

In ECMAScript 2019 (ES10), two new array methods were introduced: Array.prototype.flat() and Array.prototype.flatMap(). Both methods are useful for working with nested arrays and simplifying array manipulations. Here’s a breakdown of each:

Array.prototype.flat()

The flat() method is used to flatten nested arrays. It creates a new array with all sub-array elements concatenated into it recursively up to the specified depth.

const arr = [1, 2, [3, 4, [5, 6]]];

console.log(arr.flat());          // Output: [1, 2, 3, 4, [5, 6]]
console.log(arr.flat(2));        // Output: [1, 2, 3, 4, 5, 6]
console.log(arr.flat(Infinity)); // Output: [1, 2, 3, 4, 5, 6]

In the above example, arr.flat(2) flattens the array to a depth of 2, while arr.flat(Infinity) fully flattens the array regardless of how deeply nested it is.

Array.prototype.flatMap()

The flatMap() method first maps each element using a mapping function, then flattens the result into a new array. It combines the functionality of map() and flat().

const arr = [1, 2, 3, 4];

const result = arr.flatMap(x => [x, x * 2]);
console.log(result); // Output: [1, 2, 2, 4, 3, 6, 4, 8]

In this example, flatMap() first maps each element x to an array [x, x * 2] and then flattens the result by one level, producing a single array.

Key Differences

flat() is used to flatten an existing array structure to a specified depth, while flatMap() is used to both transform and flatten an array.

flat() does not alter the original array but returns a new one, while flatMap() combines mapping and flattening into one operation.

These methods are particularly useful when dealing with complex array structures and data transformations, helping to simplify code and improve readability.

2. Array.prototype.slice() with String and Symbol keys

In ECMAScript 2019 (ES10), the Array.prototype.slice() method saw improvements, particularly in handling objects with String and Symbol keys.

Array.prototype.slice()

The slice() method returns a shallow copy of a portion of an array into a new array object selected from start to end (end not included) where start and end represent the index positions in the original array. The original array will not be modified.

const arr = [1, 2, 3, 4, 5];

console.log(arr.slice(1, 3)); // Output: [2, 3]
console.log(arr.slice(-3, -1)); // Output: [3, 4]
console.log(arr.slice()); // Output: [1, 2, 3, 4, 5]

Handling String and Symbol Keys

Starting with ES10, the slice() method’s handling of non-numeric keys in arrays—such as String and Symbol keys—was clarified. These keys are not considered in slicing operations. The slice() method affects only numeric indices, and String and Symbol keys are ignored.

Example with String and Symbol Keys:

const arr = [1, 2, 3];
arr['foo'] = 'bar';
arr[Symbol('baz')] = 'qux';

console.log(arr.slice(0, 2)); // Output: [1, 2]
console.log(arr.foo);         // Output: 'bar'
console.log(arr[Symbol('baz')]); // Output: undefined

In this example, arr.slice(0, 2) produces [1, 2], as expected, ignoring the String and Symbol keys (foo and Symbol('baz')).

Key Points

  • slice() does not modify the original array but returns a new array.
  • Only numeric indices are considered for slicing operations; String and Symbol keys are not included.
  • Negative indices are allowed for both begin and end parameters.

This behavior ensures that slice() works predictably when dealing with arrays that might have additional properties or non-numeric keys.

3. Object.fromEntries()

In ECMAScript 2019 (ES10), a new method Object.fromEntries() was introduced. This method is useful for converting a list of key-value pairs into an object.

Object.fromEntries()

The Object.fromEntries() method takes an iterable (such as an array of key-value pairs) and returns a new object whose properties are derived from the iterable.

Basic Example
const entries = [['foo', 'bar'], ['baz', 42]];
const obj = Object.fromEntries(entries);
console.log(obj); // Output: { foo: 'bar', baz: 42 }
Using Map
const map = new Map([['foo', 'bar'], ['baz', 42]]);
const obj = Object.fromEntries(map);
console.log(obj); // Output: { foo: 'bar', baz: 42 }
From Object.entries()
const obj = { a: 1, b: 2, c: 3 };
const entries = Object.entries(obj);
const newObj = Object.fromEntries(entries);
console.log(newObj); // Output: { a: 1, b: 2, c: 3 }

Practical Uses

Transforming Data

Object.fromEntries() can be used to transform or filter data structures. For example, you might want to filter entries from an object and then reconstruct it.

const obj = { a: 1, b: 2, c: 3, d: 4 };
const filteredEntries = Object.entries(obj).filter(([key, value]) => value % 2 === 0);
const filteredObj = Object.fromEntries(filteredEntries);
console.log(filteredObj); // Output: { b: 2, d: 4 }
Converting Map to Object

If you have a Map and want to convert it to a plain object, Object.fromEntries() provides a clean and straightforward way to do this.

const map = new Map([['key1', 'value1'], ['key2', 'value2']]);
const obj = Object.fromEntries(map);
console.log(obj); // Output: { key1: 'value1', key2: 'value2' }

By using Object.fromEntries(), you can handle data transformation tasks more efficiently and with cleaner code.

4. String.prototype.trimStart() and String.prototype.trimEnd()

In ECMAScript 2019 (ES10), two new methods were introduced for string manipulation: String.prototype.trimStart() and String.prototype.trimEnd(). These methods provide more fine-grained control over whitespace removal from the beginning and end of strings.

String.prototype.trimStart()

The trimStart() method (also known as trimLeft()) removes whitespace from the beginning of a string. It is useful when you need to trim leading whitespace characters but leave trailing whitespace intact.

const str = '   Hello, world!   ';
console.log(str.trimStart()); // Output: 'Hello, world!   '

In this example, trimStart() removes the leading spaces before “Hello, world!” but keeps the trailing spaces.

String.prototype.trimEnd()

The trimEnd() method (also known as trimRight()) removes whitespace from the end of a string. This method is handy when you want to trim trailing whitespace characters while leaving leading whitespace intact.

const str = '   Hello, world!   ';
console.log(str.trimEnd()); // Output: '   Hello, world!'

Here, trimEnd() removes the trailing spaces after “Hello, world!” but leaves the leading spaces untouched.

Comparison with trim()

The trim() method, which was introduced in ECMAScript 5 (ES5), removes whitespace from both the beginning and end of a string. The trimStart() and trimEnd() methods offer more specific control when you need to handle leading and trailing whitespace separately.

Example with trim()
const str = '   Hello, world!   ';
console.log(str.trim()); // Output: 'Hello, world!'

In this example, trim() removes both leading and trailing spaces.

Summary

  • String.prototype.trimStart(): Removes whitespace from the start of the string.
  • String.prototype.trimEnd(): Removes whitespace from the end of the string.
  • Both methods are useful for more granular control over string trimming compared to the trim() method, which removes whitespace from both ends.

5. Symbol.prototype.description

In ECMAScript 2019 (ES10), a new property was added to the Symbol object: Symbol.prototype.description. This property provides a straightforward way to access the description of a Symbol.

Symbol.prototype.description

The description property returns the description of the Symbol instance. A Symbol can be created with an optional description, which is a string that serves as a label or identifier for the symbol. The description property allows you to retrieve this description without directly accessing the internal Symbol description via toString().

const sym1 = Symbol('description');
const sym2 = Symbol();

console.log(sym1.description); // Output: 'description'
console.log(sym2.description); // Output: undefined

description: This property is read-only and returns the string description of the Symbol if one was provided when the symbol was created. If no description was given, it returns undefined.

Accessing the Description: Prior to ES10, the description could be accessed through the Symbol‘s toString() method. The description property provides a more direct way to get this information.

Example with toString()

const sym = Symbol('example');
console.log(sym.toString()); // Output: 'Symbol(example)'
console.log(sym.description); // Output: 'example'

In this example, sym.toString() returns a string that includes the description along with the Symbol identifier, while sym.description directly provides just the description part.

Summary

  • Symbol.prototype.description is a property that returns the optional description of a Symbol.
  • It simplifies accessing the description of a Symbol, which was previously done by parsing the result of Symbol.prototype.toString().
  • If no description is provided when creating the Symbol, the property returns undefined.

6. Function.prototype.toString()

In ECMAScript 2019 (ES10), the Function.prototype.toString() method was updated to provide more consistent behavior across different JavaScript environments. This method returns a string representing the source code of the function.

Function.prototype.toString()

The toString() method returns a string that represents the source code of the function. In ES10, the specification for toString() was clarified to ensure that it always returns the full function source code, including the function’s body.

Behavior in ES10
  1. Consistent Source Code: The update in ES10 ensures that toString() returns the complete function code, including the body of the function. This is in contrast to some older environments where toString() might omit parts of the function’s code or return it in a more abstract form.
  2. Arrow Functions: For arrow functions, toString() will return the function in the form of an arrow function expression.
  3. Function Names: The returned string includes the function name and its parameters as well.
Named Function
function greet(name) {
  return `Hello, ${name}`;
}

console.log(greet.toString()); // Output: 'function greet(name) { return `Hello, ${name}`; }'
Anonymous Function
const add = function(a, b) {
  return a + b;
};

console.log(add.toString()); // Output: 'function(a, b) { return a + b; }'
Arrow Function
const multiply = (x, y) => x * y;

console.log(multiply.toString()); // Output: '(x, y) => x * y'
Usage Considerations

Debugging: toString() is often used in debugging or logging scenarios where it’s useful to inspect the source code of functions.

Serialization: In some cases, toString() can be used to serialize functions, although this is generally not recommended for production code due to potential security and maintainability issues.

Browser Differences: Prior to ES10, the output of toString() could vary between different JavaScript engines and browsers. The ES10 specification aimed to standardize this behavior.

Summary
  • Function.prototype.toString() returns a string representation of the function’s source code, including its name, parameters, and body.
  • ES10 ensures that this method consistently returns the full function code across different JavaScript environments.
  • Useful for debugging, inspecting function code, and certain serialization tasks, though caution is advised when using it for production purposes.

7. Improved regular expressions

In ECMAScript 2019 (ES10), several enhancements were introduced to regular expressions, improving their functionality and usability. These updates include new features and refinements that make working with regular expressions more powerful and convenient.

Key Improvements in Regular Expressions in ES10

s' (dotAll) Flag
  • Description: The s flag, also known as the dotAll flag, allows the dot (.) in a regular expression to match newline characters (\n). This was not possible with previous versions where the dot character did not match newlines.
  • Syntax: /pattern/s
const regex = /hello.world/s;
const str = 'hello\nworld';
console.log(regex.test(str)); // Output: true
Named Capturing Groups
  • Description: Named capturing groups allow you to name capturing groups in a regular expression, making it easier to refer to and access captured substrings by name rather than by index.
  • Syntax: /(?<name>pattern)/
const regex = /(?<first>\d{3})-(?<second>\d{2})-(?<third>\d{4})/;
const str = '123-45-6789';
const result = regex.exec(str);
console.log(result.groups.first); // Output: '123'
console.log(result.groups.second); // Output: '45'
console.log(result.groups.third); // Output: '6789'
Lookbehind Assertions
  • Description: Lookbehind assertions allow you to match a pattern only if it is preceded by another pattern. This feature makes it possible to perform more complex pattern matching.
  • Syntax: /(?<=pattern)match/ for positive lookbehind, /(?<!pattern)match/ for negative lookbehind.
const regex = /(?<=@)\w+/;
const str = 'contact@example.com';
console.log(str.match(regex)); // Output: ['example']
Unicode Property Escapes
  • Description: Unicode property escapes allow matching characters based on their Unicode properties, such as their category or script. This enhances the ability to create regular expressions that work across various languages and scripts.
  • Syntax: \p{Property=Value} or \p{Value} for matching Unicode properties.
const regex = /\p{Script=Latin}/u;
const str = 'Hello';
console.log(regex.test(str)); // Output: true
RegExp.prototype.flags Property
  • Description: The flags property returns a string containing the flags of the regular expression. This provides an easy way to retrieve the flags used in a regular expression.
  • Syntax: regex.flags
const regex = /abc/gi;
console.log(regex.flags); // Output: 'gi'

Summary

  • s' Flag (dotAll): Allows the dot (.) to match newline characters.
  • Named Capturing Groups: Facilitates referencing captured groups by name rather than index.
  • Lookbehind Assertions: Supports matching patterns preceded by another pattern.
  • Unicode Property Escapes: Enables matching based on Unicode properties.
  • RegExp.prototype.flags: Provides a string of the flags used in the regular expression.

These enhancements make regular expressions in ES10 more versatile and powerful, enabling more sophisticated pattern matching and text processing capabilities.

If you missed my recent posts on ES6 (ECMAScript 2015), ES7 (ECMAScript 2016), ES8 (ECMAScript 2017) and ES9 (ECMAScript 2018), you can catch up by clicking the links below to explore their features and updates.

Click here to explore ES6 (ECMAScript 2015)
Click here to explore ES7 (ECMAScript 2016)
Click here to explore ES8 (ECMAScript 2017)
Click here to explore ES9 (ECMAScript 2018)

Happy coding!

FAQs

Symbol.prototype.description is a new accessor property that returns the description of a Symbol object. This provides a way to get the optional string description of a symbol.

const sym = Symbol('description');
console.log(sym.description); // 'description'

  • Array.prototype.flat(depth) flattens nested arrays up to the specified depth. By default, it flattens to a depth of 1.
  • Array.prototype.flatMap(callback) maps each element using a mapping function and then flattens the result into a new array.

let arr = [1, [2, [3, [4]]]];
console.log(arr.flat(2)); // [1, 2, 3, [4]]

let arr2 = [1, 2, 3, 4];
console.log(arr2.flatMap(x => [x * 2])); // [2, 4, 6, 8]

Object.fromEntries() creates a new object from an iterable of key-value pairs (like an array of arrays or a Map). This is useful for converting data structures into objects.

let entries = [['name', 'Alice'], ['age', 25]];
let obj = Object.fromEntries(entries);
console.log(obj); // { name: 'Alice', age: 25 }

  • String.prototype.trimStart() removes whitespace from the beginning of a string.
  • String.prototype.trimEnd() removes whitespace from the end of a string.

let str = ' Hello World! ';
console.log(str.trimStart()); // 'Hello World! '
console.log(str.trimEnd()); // ' Hello World!'

Optional Catch Binding allows you to omit the error parameter in a catch block if you do not need to use it. This simplifies error handling code when the error is not utilized.

try {
// Code that may throw an error
} catch {
// Error handling without using the error parameter
}

Function.prototype.toString() was standardized to return a more consistent and reliable source code representation of the function. This change ensures better support for tools and debugging.

function example() { return 42; }
console.log(example.toString()); // 'function example() { return 42; }'

ES10 improved JSON.stringify() to handle certain edge cases more consistently, particularly with special objects and circular references. This helps ensure predictable behavior when serializing objects to JSON.

To check if your environment supports ECMAScript 2019 features, you can use feature detection tools such as Babel for transpiling code to ensure compatibility. Additionally, check compatibility tables on resources like MDN Web Docs or Can I use for up-to-date information on feature support.

3 thoughts on “Everything You Need to Know About ECMAScript 2019 (ES10)

Leave a Reply

Your email address will not be published. Required fields are marked *