7 min read
ECMAScript 2018 was released in June 2018, adds fewer features than major editions (ES2016, ES2017) used to. These new changes are Asynchronous Iteration, Rest/Spread Properties, new features to Regular Expressions and a revision to template literals.
With synchronous iterations, we are able to iterate data from a synchronous source, but not when data is from an asynchronous source, for example https fetch.
We will recap synchronous iteration and then asynchronous iteration.
Synchronous iteration was introduced with ES6 and have the following components
Symbol.iterator
[Symbol.iterator]()
. It has a method .next()
that returns the next element..next()
that has 2 properties:
If you need some extra info about Iterator pattern. Check this link
The previously explained way of iterating is synchronous, it doesn’t work for asynchronous sources. For example, in the following code, fetchCoinValues()
can't deliver its asynchronous data via synchronous iteration:
for (const coinValue of fetchCoinValues(['ETH','XMR','BTC','LTC','BAT'])) {
console.log(coinValue);
}
ES2018 specifies a new protocol for async iteration:
Symbol.asyncIterator
..next()
of an async iterator returns a Promise
or NextElement
.With this new protocol, we are able to await
for an asynchronous fetched coinValue
for await (const coinValue of fetchCoinValues(['ETH', 'XMR', 'BTC', 'LTC', 'BAT'])) {
console.log(coinValue);
}
We have a new (or two) operator(s).
...
) is used in object destructuring....
) in object literals.(Yes, looks similar =))
Inside object destructuring patterns, the rest operator (...
) copies all enumerable own properties of the destructuring source (foo
) into its operand( const { }
), except those that were already mentioned in the object literal.
const foo = {
empanadas: 6,
milanesas: 2,
yerba: '1 Kg'
};
const { yerba, ...rest } = foo;
console.log(yerba); // Prints "1 Kg"
console.log(rest); // Prints {empanadas: 6, milanesas: 2}
We can do it inside of a function, to handle named parameters
function func({ yerba, ...rest }) {
// rest operator
/**
* You can eat empanadas and drink mate here using property rest.empanadas
* and rest.yerba.
*/
}
We can use the rest operator at most once and it must be last.
const { ...rest, yerba } = foo; // syntax error, must be last.
const { ...rest, ...others } = foo; // syntax error, 2 operators.
In right side object literals ({...foo, water: "1 L"}
), the spread operator (...
) inserts all enumerable own properties of its operand (foo
) into the object created (bar
) via the literal:
const foo = {
empanadas: 6,
milanesas: 2,
yerba: '1 Kg'
};
const bar = { ...foo, water: '1 L' };
console.log(bar); // Prints
/**
* {
* empanadas: 6,
* milanesas: 2,
* yerba: "1 Kg",
* water: "1 L",
**/
If we have conflicts with keys (empanadas
), last one wins (empanadas : 12
). This is useful to override default keys.
const foo = {
empanadas: 6,
milanesas: 2,
yerba: '1 Kg'
};
const bar = { ...foo, empanadas: 12 };
console.log(bar); // Prints
/**
* {
* empanadas: 12,
* milanesas: 2,
* yerba: "1 Kg",
**/
This release brings 4 new features to regulars expressions
Dot (.
) in regular expressions don't match line terminators
console.log(/^.$/.test('\n')); // Prints false
DotAll flag (/s
) allows .
in a regExp to match line terminators ( /n
) and emojis (😁
)
console.log(/^.$/s.test('😁')); // Prints true
Unicode standard assigns properties to each symbol, like White_Space
. Now, we can access this properties inside of RegEXp using flag /u
.
const result = /^\p{White_Space}+$/u.test('3 empanadas');
console.log(result); // Prints true
This new feature makes RegExp much more readable.
A lookbehind assertion is denoted by ?<=
, and enables you to match a pattern based on the substring that precedes the pattern. For example,
const regExp = /(?<=\$|£|€)\d+(\.\d*)?/;
console.log(regExp.exec('199')); // Prints null
console.log(regExp.exec('$199')[0]); // Prints 199
Also, we have the negative version replacing =
with !
, that is ?<!
const negativeRegExp = /(?<!\$|£|€)\d+(\.\d*)?/;
Remember that the substring matched by the lookbehind expression isn't captured and isn't part of the result.
Named capture groups enable you to take apart a string with a regular expression.
Matching a regular expression returns an object. If a fragment of the regular expression is inside a parentheses, is a capture group and it's stored in the matched object.
Prior to ES2018, all capture groups were indexed by indexes
const eventDate = /([0-9]{4})-([0-9]{2})-([0-9]{2})/;
const matchedObject = eventDate.exec('2019-04-03');
console.log(matchedObject[1]); // 2019
console.log(matchedObject[2]); // 04
console.log(matchedObject[3]); // 23
After this update, we can name groups year, month and day
using <groupName>
, that are stored inside groups
key:
const eventDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/;
const matchedObject = eventDate.exec('2019-04-03');
console.log(matchedObject.groups.year); // Prints 2019
console.log(matchedObject.groups.month); // Prints 04
console.log(matchedObject.groups.day); // Prints 03
We can combine with destructuring!
const {
groups: { day, month }
} = matchedObject.exec('2019-04-03');
console.log(day); // Prints 03
With this update is easier to find groups by ID(year, month, day), each group is self descriptive.
ES2018 introduces a new callback that is always executed, no matter if then or catch is called.
fetch(url)
.then()
.catch()
.finally(() => console.log(`I'm always called!`));
.finally
is useful when you need to do some clean up after the operation has finished regardless of whether or not it succeeded,for example to close a connection.
When a template literal (${food} is tasty
) is immediately preceded by an expression (fn'${food} is tasty'
), it is called a tagged template literal. A tagged template comes in handy when you want to parse a template literal with a function(fn
). Consider:
function fn(string, substitute) {
if (substitute === 'empanadas') {
substitute = 'milanesas with mashed potatoes';
}
return substitute + string[1];
}
const food = 'empanadas';
const result = fn`${food} are tasty!`;
console.log(result); // Prints milanesas with mashed potatoes are tasty!
It has problems with escape sequences like hex (\x
), unicode (\u
), or octal (\
followed by a digit). Strings like C:\xxx
or \ubuntu
are considered invalid escape sequences and throws a SyntaxError.
With ES2018 revision, each illegal sequence is undefined
function fn(string, substitute) {
console.log(substitute); // Prints escape sequences:
console.log(string[1]); // Prints undefined
}
const str = 'escape sequences:';
const result = fn`${str} \ubuntu C:\xxx\uuu`;
Keep in mind that using illegal escape sequences in a regular template literal still causes an error:
const result = `\ubuntu`; // SyntaxError: Invalid Unicode escape sequence
This is a lot to digest, but each word, revision, feature and release will improve or programming skills. Remember that, having this in mind,and using it, our code will be shorter and cleaner than before.