{"id":280621,"date":"2019-01-09T08:19:41","date_gmt":"2019-01-09T15:19:41","guid":{"rendered":"http:\/\/css-tricks.com\/?p=280621"},"modified":"2020-11-23T22:33:17","modified_gmt":"2020-11-24T06:33:17","slug":"new-es2018-features-every-javascript-developer-should-know","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/new-es2018-features-every-javascript-developer-should-know\/","title":{"rendered":"New ES2018 Features Every JavaScript Developer Should Know"},"content":{"rendered":"
The ninth edition of the ECMAScript standard, officially known as ECMAScript 2018 (or ES2018 for short), was released in June 2018. Starting with ES2016, new versions of ECMAScript specifications are released yearly rather than every several years and add fewer features than major editions used to. The newest edition of the standard continues the yearly release cycle by adding four new RegExp<\/code> features, rest\/spread properties, asynchronous iteration, and Promise.prototype.finally<\/code>. Additionally, ES2018 drops the syntax restriction of escape sequences from tagged templates.<\/p>\nThese new changes are explained in the subsections that follow.<\/p>\n
<\/p>\n
The Rest\/Spread Properties<\/h3>\n
One of the most interesting features added to ES2015 was the spread operator. This operator makes copying and merging arrays a lot simpler. Rather than calling the concat()<\/code> or slice()<\/code> method, you could use the ...<\/code> operator:<\/p>\nconst arr1 = [10, 20, 30];\n\n\/\/ make a copy of arr1\nconst copy = [...arr1];\n\nconsole.log(copy); \/\/ \u2192 [10, 20, 30]\n\nconst arr2 = [40, 50];\n\n\/\/ merge arr2 with arr1\nconst merge = [...arr1, ...arr2];\n\nconsole.log(merge); \/\/ \u2192 [10, 20, 30, 40, 50]<\/code><\/pre>\nThe spread operator also comes in handy in situations where an array must be passed in as separate arguments to a function. For example:<\/p>\n
const arr = [10, 20, 30]\n\n\/\/ equivalent to\n\/\/ console.log(Math.max(10, 20, 30));\nconsole.log(Math.max(...arr)); \/\/ \u2192 30<\/code><\/pre>\nES2018 further expands this syntax by adding spread properties to object literals. With the spread properties you can copy own enumerable properties of an object onto a new object. Consider the following example:<\/p>\n
const obj1 = {\n a: 10,\n b: 20\n};\n\nconst obj2 = {\n ...obj1,\n c: 30\n};\n\nconsole.log(obj2); \/\/ \u2192 {a: 10, b: 20, c: 30}<\/code><\/pre>\nIn this code, the ...<\/code> operator is used to retrieve the properties of obj1<\/code> and assign them to obj2<\/code>. Prior to ES2018, attempting to do so would throw an error. If there are multiple properties with the same name, the property that comes last will be used:<\/p>\nconst obj1 = {\n a: 10,\n b: 20\n};\n\nconst obj2 = {\n ...obj1,\n a: 30\n};\n\nconsole.log(obj2); \/\/ \u2192 {a: 30, b: 20}<\/code><\/pre>\nSpread properties also provide a new way to merge two or more objects, which can be used as an alternative to the Object.assign()<\/code> method:<\/p>\nconst obj1 = {a: 10};\nconst obj2 = {b: 20};\nconst obj3 = {c: 30};\n\n\/\/ ES2018\nconsole.log({...obj1, ...obj2, ...obj3}); \/\/ \u2192 {a: 10, b: 20, c: 30}\n\n\/\/ ES2015\nconsole.log(Object.assign({}, obj1, obj2, obj3)); \/\/ \u2192 {a: 10, b: 20, c: 30}<\/code><\/pre>\nNote, however, that spread properties do not always produce the same result as Object.assign()<\/code>. Consider the following code:<\/p>\nObject.defineProperty(Object.prototype, 'a', {\n set(value) {\n console.log('set called!');\n }\n});\n\nconst obj = {a: 10};\n\nconsole.log({...obj}); \n\/\/ \u2192 {a: 10}\n\nconsole.log(Object.assign({}, obj)); \n\/\/ \u2192 set called!\n\/\/ \u2192 {}<\/code><\/pre>\nIn this code, the Object.assign()<\/code> method executes the inherited setter property. Conversely, the spread properties simply ignore the setter.<\/p>\nIt’s important to remember that spread properties only copy enumerable properties. In the following example, the type<\/code> property won\u2019t show up in the copied object because its enumerable<\/code> attribute is set to false<\/code>:<\/p>\nconst car = {\n color: 'blue'\n};\n\nObject.defineProperty(car, 'type', {\n value: 'coupe',\n enumerable: false\n});\n\nconsole.log({...car}); \/\/ \u2192 {color: \"blue\"}<\/code><\/pre>\nInherited properties are ignored even if they are enumerable:<\/p>\n
const car = {\n color: 'blue'\n};\n\nconst car2 = Object.create(car, {\n type: {\n value: 'coupe',\n enumerable: true,\n }\n});\n\nconsole.log(car2.color); \/\/ \u2192 blue\nconsole.log(car2.hasOwnProperty('color')); \/\/ \u2192 false\n\nconsole.log(car2.type); \/\/ \u2192 coupe\nconsole.log(car2.hasOwnProperty('type')); \/\/ \u2192 true\n\nconsole.log({...car2}); \/\/ \u2192 {type: \"coupe\"}<\/code><\/pre>\nIn this code, car2<\/code> inherits the color<\/code> property from car<\/code>. Because spread properties only copy the own properties of an object, color<\/code> is not included in the return value.<\/p>\nKeep in mind that spread properties can only make a shallow copy of an object. If a property holds an object, only the reference to the object will be copied:<\/p>\n
const obj = {x: {y: 10}};\nconst copy1 = {...obj}; \nconst copy2 = {...obj}; \n\nconsole.log(copy1.x === copy2.x); \/\/ \u2192 true<\/code><\/pre>\nThe x<\/code> property in copy1<\/code> refers to the same object in memory that x<\/code> in copy2<\/code> refers to, so the strict equality operator returns true<\/code>.<\/p>\nAnother useful feature added to ES2015 was rest parameters, which enabled JavaScript programmers to use ...<\/code> to represent values as an array. For example:<\/p>\nconst arr = [10, 20, 30];\nconst [x, ...rest] = arr;\n\nconsole.log(x); \/\/ \u2192 10\nconsole.log(rest); \/\/ \u2192 [20, 30]<\/code><\/pre>\nHere, the first item in arr<\/code> is assigned to x<\/code>, and remaining elements are assigned to the rest<\/code> variable. This pattern, called array destructuring, became so popular that the Ecma Technical Committee decided to bring similar functionality to objects:<\/p>\nconst obj = {\n a: 10,\n b: 20,\n c: 30\n};\n\nconst {a, ...rest} = obj;\n\nconsole.log(a); \/\/ \u2192 10\nconsole.log(rest); \/\/ \u2192 {b: 20, c: 30}<\/code><\/pre>\nThis code uses the rest properties in a destructuring assignment to copy the remaining own enumerable properties into a new object. Note that rest properties must always appear at the end of the object, otherwise an error is thrown:<\/p>\n
const obj = {\n a: 10,\n b: 20,\n c: 30\n};\n\nconst {...rest, a} = obj; \/\/ \u2192 SyntaxError: Rest element must be last element<\/code><\/pre>\nAlso keep in mind that using multiple rest syntaxes in an object causes an error unless they are nested:<\/p>\n
const obj = {\n a: 10,\n b: {\n x: 20,\n y: 30,\n z: 40\n }\n};\n\nconst {b: {x, ...rest1}, ...rest2} = obj; \/\/ no error\n\nconst {...rest, ...rest2} = obj; \/\/ \u2192 SyntaxError: Rest element must be last element<\/code><\/pre>\nSupport for Rest\/Spread Properties<\/h4>\n
\n\n\nChrome<\/th>\n | Firefox<\/th>\n | Safari<\/th>\n | Edge<\/th>\n<\/tr>\n<\/thead>\n |
\n\n60<\/td>\n | 55<\/td>\n | 11.1<\/td>\n | No<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n\n\n\nChrome Android<\/th>\n | Firefox Android<\/th>\n | iOS Safari<\/th>\n | Edge Mobile<\/th>\n | Samsung Internet<\/th>\n | Android Webview<\/th>\n<\/tr>\n<\/thead>\n | \n\n60<\/td>\n | 55<\/td>\n | 11.3<\/td>\n | No<\/td>\n | 8.2<\/td>\n | 60<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n Node.js:<\/strong><\/strong><\/p>\n\n- 8.0.0 (requires the
--harmony<\/code> runtime flag)<\/li>\n- 8.3.0 (full support)<\/li>\n<\/ul>\n
Asynchronous Iteration<\/h3>\nIterating over a collection of data is an important part of programming. Prior to ES2015, JavaScript provided statements such as for<\/code>, for...in<\/code>, and while<\/code>, and methods such as map()<\/code>, filter()<\/code>, and forEach()<\/code> for this purpose. To enable programmers to process the elements in a collection one at a time, ES2015 introduced the iterator interface.<\/p>\nAn object is iterable if it has a Symbol.iterator<\/code> property. In ES2015, strings and collections objects such as Set<\/code>, Map<\/code>, and Array<\/code> come with a Symbol.iterator<\/code> property and thus are iterable. The following code gives an example of how to access the elements of an iterable one at a time:<\/p>\nconst arr = [10, 20, 30];\nconst iterator = arr[Symbol.iterator]();\n \nconsole.log(iterator.next()); \/\/ \u2192 {value: 10, done: false}\nconsole.log(iterator.next()); \/\/ \u2192 {value: 20, done: false}\nconsole.log(iterator.next()); \/\/ \u2192 {value: 30, done: false}\nconsole.log(iterator.next()); \/\/ \u2192 {value: undefined, done: true}<\/code><\/pre>\nSymbol.iterator<\/code> is a well-known symbol specifying a function that returns an iterator. The primary way to interact with an iterator is the next()<\/code> method. This method returns an object with two properties: value<\/code> and done<\/code>. The value<\/code> property contains the value of the next element in the collection. The done<\/code> property contains either true<\/code> or false<\/code> denoting whether or not the end of the collection has reached.<\/p>\nBy default, a plain object is not iterable, but it can become iterable if you define a Symbol.iterator<\/code> property on it, as in this example:<\/p>\nconst collection = {\n a: 10,\n b: 20,\n c: 30,\n [Symbol.iterator]() {\n const values = Object.keys(this);\n let i = 0;\n return {\n next: () => {\n return {\n value: this[values[i++]],\n done: i > values.length\n }\n }\n };\n }\n};\n\nconst iterator = collection[Symbol.iterator]();\n \nconsole.log(iterator.next()); \/\/ \u2192 {value: 10, done: false}\nconsole.log(iterator.next()); \/\/ \u2192 {value: 20, done: false}\nconsole.log(iterator.next()); \/\/ \u2192 {value: 30, done: false}\nconsole.log(iterator.next()); \/\/ \u2192 {value: undefined, done: true}<\/code><\/pre>\nThis object is iterable because it defines a Symbol.iterator<\/code> property. The iterator uses the Object.keys()<\/code> method to get an array of the object’s property names and then assigns it to the values<\/code> constant. It also defines a counter variable and gives it an initial value of 0. When the iterator is executed it returns an object that contains a next()<\/code> method. Each time the next()<\/code> method is called, it returns a {value, done}<\/code> pair, with value<\/code> holding the next element in the collection and done<\/code> holding a Boolean indicating if the iterator has reached the need of the collection.<\/p>\nWhile this code works perfectly, it\u2019s unnecessarily complicated. Fortunately, using a generator function can considerably simplify the process:<\/p>\n const collection = {\n a: 10,\n b: 20,\n c: 30,\n [Symbol.iterator]: function * () {\n for (let key in this) {\n yield this[key];\n }\n }\n};\n\nconst iterator = collection[Symbol.iterator]();\n \nconsole.log(iterator.next()); \/\/ \u2192 {value: 10, done: false}\nconsole.log(iterator.next()); \/\/ \u2192 {value: 20, done: false}\nconsole.log(iterator.next()); \/\/ \u2192 {value: 30, done: false}\nconsole.log(iterator.next()); \/\/ \u2192 {value: undefined, done: true}<\/code><\/pre>\nInside this generator, a for...in<\/code> loop is used to enumerate over the collection and yield the value of each property. The result is exactly the same as the previous example, but it\u2019s greatly shorter.<\/p>\nA downside of iterators is that they are not suitable for representing asynchronous data sources. ES2018\u2019s solution to remedy that is asynchronous iterators and asynchronous iterables. An asynchronous iterator differs from a conventional iterator in that, instead of returning a plain object in the form of {value, done}<\/code>, it returns a promise that fulfills to {value, done}<\/code>. An asynchronous iterable defines a Symbol.asyncIterator<\/code> method (instead of Symbol.iterator<\/code>) that returns an asynchronous iterator.<\/p>\nAn example should make this clearer:<\/p>\n const collection = {\n a: 10,\n b: 20,\n c: 30,\n [Symbol.asyncIterator]() {\n const values = Object.keys(this);\n let i = 0;\n return {\n next: () => {\n return Promise.resolve({\n value: this[values[i++]], \n done: i > values.length\n });\n }\n };\n }\n};\n\nconst iterator = collection[Symbol.asyncIterator]();\n \nconsole.log(iterator.next().then(result => {\n console.log(result); \/\/ \u2192 {value: 10, done: false}\n}));\n\nconsole.log(iterator.next().then(result => {\n console.log(result); \/\/ \u2192 {value: 20, done: false} \n}));\n\nconsole.log(iterator.next().then(result => {\n console.log(result); \/\/ \u2192 {value: 30, done: false} \n}));\n\nconsole.log(iterator.next().then(result => {\n console.log(result); \/\/ \u2192 {value: undefined, done: true} \n}));<\/code><\/pre>\nNote that it\u2019s not possible to use an iterator of promises to achieve the same result. Although a normal, synchronous iterator can asynchronously determine the values, it still needs to determine the state of “done” synchronously.<\/p>\n Again, you can simplify the process by using a generator function, as shown below:<\/p>\n const collection = {\n a: 10,\n b: 20,\n c: 30,\n [Symbol.asyncIterator]: async function * () {\n for (let key in this) {\n yield this[key];\n }\n }\n};\n\nconst iterator = collection[Symbol.asyncIterator]();\n \nconsole.log(iterator.next().then(result => {\n console.log(result); \/\/ \u2192 {value: 10, done: false}\n}));\n\nconsole.log(iterator.next().then(result => {\n console.log(result); \/\/ \u2192 {value: 20, done: false} \n}));\n\nconsole.log(iterator.next().then(result => {\n console.log(result); \/\/ \u2192 {value: 30, done: false} \n}));\n\nconsole.log(iterator.next().then(result => {\n console.log(result); \/\/ \u2192 {value: undefined, done: true} \n}));<\/code><\/pre>\nNormally, a generator function returns a generator object with a next()<\/code> method. When next()<\/code> is called it returns a {value, done}<\/code> pair whose value<\/code> property holds the yielded value. An async generator does the same thing except that it returns a promise that fulfills to {value, done}<\/code>.<\/p>\nAn easy way to iterate over an iterable object is to use the for...of<\/code> statement, but for...of<\/code> doesn’t work with async iterables as value<\/code> and done<\/code> are not determined synchronously. For this reason, ES2018 provides the for...await...of<\/code> statement. Let\u2019s look at an example:<\/p>\nconst collection = {\n a: 10,\n b: 20,\n c: 30,\n [Symbol.asyncIterator]: async function * () {\n for (let key in this) {\n yield this[key];\n }\n }\n};\n\n(async function () {\n for await (const x of collection) {\n console.log(x);\n }\n})();\n\n\/\/ logs:\n\/\/ \u2192 10\n\/\/ \u2192 20\n\/\/ \u2192 30<\/code><\/pre>\nIn this code, the for...await...of<\/code> statement implicitly calls the Symbol.asyncIterator<\/code> method on the collection object to get an async iterator. Each time through the loop, the next()<\/code> method of the iterator is called, which returns a promise. Once the promise is resolved, the value<\/code> property of the resulting object is read to the x<\/code> variable. The loop continues until the done<\/code> property of the returned object has a value of true<\/code>.<\/p>\nKeep in mind that the for...await...of<\/code> statement is only valid within async generators and async functions. Violating this rule results in a SyntaxError<\/code>.<\/p>\nThe next()<\/code> method may return a promise that rejects. To gracefully handle a rejected promise, you can wrap the for...await...of<\/code> statement in a try...catch<\/code> statement, like this:<\/p>\nconst collection = {\n [Symbol.asyncIterator]() {\n return {\n next: () => {\n return Promise.reject(new Error('Something went wrong.'))\n }\n };\n }\n};\n\n(async function() {\n try {\n for await (const value of collection) {}\n } catch (error) {\n console.log('Caught: ' + error.message);\n }\n})();\n\n\/\/ logs:\n\/\/ \u2192 Caught: Something went wrong.<\/code><\/pre>\nSupport for Asynchronous Iterators<\/h4>\n\n\n\nChrome<\/th>\n | Firefox<\/th>\n | Safari<\/th>\n | Edge<\/th>\n<\/tr>\n<\/thead>\n | \n\n63<\/td>\n | 57<\/td>\n | 12<\/td>\n | No<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n\n\n\nChrome Android<\/th>\n | Firefox Android<\/th>\n | iOS Safari<\/th>\n | Edge Mobile<\/th>\n | Samsung Internet<\/th>\n | Android Webview<\/th>\n<\/tr>\n<\/thead>\n | \n\n63<\/td>\n | 57<\/td>\n | 12<\/td>\n | No<\/td>\n | 8.2<\/td>\n | 63<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n Node.js:<\/strong><\/p>\n\n- 8.10.0 (requires the –harmony_async_iteration flag)<\/li>\n
- 10.0.0 (full support)<\/li>\n<\/ul>\n
Promise.prototype.finally<\/h3>\nAnother exciting addition to ES2018 is the finally()<\/code> method. Several JavaScript libraries had previously implemented a similar method, which proved useful in many situations. This encouraged the Ecma Technical Committee to add finally()<\/code> to the specification. With this method, programmers will be able to execute a block of code regardless of the promise’s fate. Let\u2019s look at a simple example:<\/p>\nfetch('https:\/\/www.google.com')\n .then((response) => {\n console.log(response.status);\n })\n .catch((error) => { \n console.log(error);\n })\n .finally(() => { \n document.querySelector('#spinner').style.display = 'none';\n });<\/code><\/pre>\nThe finally()<\/code> method comes in handy when you need to do some clean up after the operation has finished regardless of whether or not it succeeded. In this code, the finally()<\/code> method simply hides the loading spinner after the data is fetched and processed. Instead of duplicating the final logic in the then()<\/code> and catch()<\/code> methods, the code registers a function to be executed once the promise is either fulfilled or rejected.<\/p>\nYou could achieve the same result by using promise.then(func, func)<\/code> rather than promise.finally(func)<\/code>, but you would have to repeat the same code in both fulfillment handler and rejection handler, or declare a variable for it:<\/p>\nfetch('https:\/\/www.google.com')\n .then((response) => {\n console.log(response.status);\n })\n .catch((error) => { \n console.log(error);\n })\n .then(final, final);\n\nfunction final() {\n document.querySelector('#spinner').style.display = 'none';\n}<\/code><\/pre>\nAs with then()<\/code> and catch()<\/code>, the finally()<\/code> method always returns a promise, so you can chain more methods. Normally, you want to use finally()<\/code> as the last chain, but in certain situations, such as when making a HTTP request, it\u2019s a good practice to chain another catch()<\/code> to deal with errors that may occur in finally()<\/code>.<\/p>\nSupport for Promise.prototype.finally<\/h4>\n\n\n\nChrome<\/th>\n | Firefox<\/th>\n | Safari<\/th>\n | Edge<\/th>\n<\/tr>\n<\/thead>\n | \n\n63<\/td>\n | 58<\/td>\n | 11.1<\/td>\n | 18<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n\n\n\nChrome Android<\/th>\n | Firefox Android<\/th>\n | iOS Safari<\/th>\n | Edge Mobile<\/th>\n | Samsung Internet<\/th>\n | Android Webview<\/th>\n<\/tr>\n<\/thead>\n | \n\n63<\/td>\n | 58<\/td>\n | 11.1<\/td>\n | No<\/td>\n | 8.2<\/td>\n | 63<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n Node.js:<\/strong><\/p>\n10.0.0 (full support)<\/p>\n New RegExp Features<\/h3>\nES2018 adds four new features to the RegExp<\/code> object, which further improves JavaScript\u2019s string processing capabilities. These features are as follows:<\/p>\n\n- s (dotAll) flag<\/li>\n
- Named capture groups<\/li>\n
- Lookbehind assertions<\/li>\n
- Unicode property escapes<\/li>\n<\/ul>\n
s (dotAll) Flag<\/h4>\nThe dot (.<\/code>) is a special character in a regular expression pattern that matches any character except line break characters such as line feed (\\n<\/code>) or carriage return (\\r<\/code>). A workaround to match all characters, including line breaks, is to use a character class with two opposite shorthands such as [\\d\\D]<\/code>. This character class tells the regular expression engine to find a character that\u2019s either a digit (\\d<\/code>) or a non-digit (\\D<\/code>). As a result, it matches any character:<\/p>\nconsole.log(\/one[\\d\\D]two\/.test('one\\ntwo')); \/\/ \u2192 true<\/code><\/pre>\nES2018 introduces a mode in which the dot can be used to achieve the same result. This mode can be activated on per-regex basis by using the s<\/code> flag:<\/p>\nconsole.log(\/one.two\/.test('one\\ntwo')); \/\/ \u2192 false\nconsole.log(\/one.two\/s.test('one\\ntwo')); \/\/ \u2192 true<\/code><\/pre>\nThe benefit of using a flag to opt in to the new behavior is backwards compatibility. So existing regular expression patterns that use the dot character are not affected.<\/p>\n Named Capture Groups<\/h4>\nIn some regular expression patterns, using a number to reference a capture group can be confusing. For example, take the regular expression \/(\\d{4})-(\\d{2})-(\\d{2})\/<\/code> which matches a date. Because date notation in American English is different from British English, it\u2019s hard to know which group refers to the day and which group refers to the month:<\/p>\nconst re = \/(\\d{4})-(\\d{2})-(\\d{2})\/;\nconst match= re.exec('2019-01-10');\n\nconsole.log(match[0]); \/\/ \u2192 2019-01-10\nconsole.log(match[1]); \/\/ \u2192 2019\nconsole.log(match[2]); \/\/ \u2192 01\nconsole.log(match[3]); \/\/ \u2192 10<\/code><\/pre>\nES2018 introduces named capture groups which uses the (?<name>...)<\/name><\/code> syntax. So, the pattern to match a date can be written in a less ambiguous manner:<\/p>\nconst re = \/(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})\/;\nconst match = re.exec('2019-01-10');\n\nconsole.log(match.groups); \/\/ \u2192 {year: \"2019\", month: \"01\", day: \"10\"}\nconsole.log(match.groups.year); \/\/ \u2192 2019\nconsole.log(match.groups.month); \/\/ \u2192 01\nconsole.log(match.groups.day); \/\/ \u2192 10<\/day><\/month><\/year><\/code><\/pre>\nYou can recall a named capture group later in the pattern by using the \\k<name><\/name><\/code> syntax. For example, to find consecutive duplicate words in a sentence, you can use \/\\b(?<dup>\\w+)\\s+\\k<dup>\\b\/<\/dup><\/dup><\/code>:<\/p>\nconst re = \/\\b(?<dup>\\w+)\\s+\\k<dup>\\b\/;\nconst match = re.exec('Get that that cat off the table!'); \n\nconsole.log(match.index); \/\/ \u2192 4\nconsole.log(match[0]); \/\/ \u2192 that that<\/dup><\/dup><\/code><\/pre>\nTo insert a named capture group into the replacement string of the replace()<\/code> method, you will need to use the $<name><\/name><\/code> construct. For example:<\/p>\nconst str = 'red & blue';\n\nconsole.log(str.replace(\/(red) & (blue)\/, '$2 & $1')); \n\/\/ \u2192 blue & red\n\nconsole.log(str.replace(\/(?<red>red) & (?<blue>blue)\/, '$<blue> & $<red>')); \n\/\/ \u2192 blue & red<\/red><\/blue><\/blue><\/red><\/code><\/pre>\nLookbehind Assertions<\/h4>\nES2018 brings lookbehind assertions to JavaScript, which have been available in other regex implementations for years. Previously, JavaScript only supported lookahead assertions. A lookbehind assertion is denoted by (?<=...)<\/code>, and enables you to match a pattern based on the substring that precedes the pattern. For example, if you want to match the price of a product in dollar, pound, or euro without capturing the currency symbol, you can use \/(?<=\\$|\u00a3|\u20ac)\\d+(\\.\\d*)?\/<\/code>:<\/p>\nconst re = \/(?<=\\$|\u00a3|\u20ac)\\d+(\\.\\d*)?\/;\n\nconsole.log(re.exec('199')); \n\/\/ \u2192 null\n\nconsole.log(re.exec('$199')); \n\/\/ \u2192 [\"199\", undefined, index: 1, input: \"$199\", groups: undefined]\n\nconsole.log(re.exec('\u20ac50')); \n\/\/ \u2192 [\"50\", undefined, index: 1, input: \"\u20ac50\", groups: undefined]<\/code><\/pre>\nThere is also a negative version of lookbehind, which is denoted by (?. A negative lookbehind allows you to match a pattern only if it is not preceded by the pattern within the lookbehind. For example, the pattern <code>\/(? matches the word available if it does not have a \"un\" prefix:<\/code><\/code><\/p>\n<code> \n<pre rel=\"JavaScript\"><code class=\"language-javascript\">const re = \/(?<\/code> \n<h4>Unicode Property Escapes<\/h4> \nES2018 provides a new type of escape sequence known as Unicode property escape, which provides support for full Unicode in regular expressions. Suppose you want to match the Unicode character \u325b in a string. Although \u325b is considered a number, you can\u2019t match it with the <code>\\d<\/code> shorthand character class because it only supports ASCII [0-9] characters. Unicode property escapes, on the other hand, can be used to match any decimal number in Unicode:<\/p>\nconst str = '\u325b';\n\nconsole.log(\/\\d\/u.test(str)); \/\/ \u2192 false\nconsole.log(\/\\p{Number}\/u.test(str)); \/\/ \u2192 true<\/code><\/pre>\nSimilarly, if you want to match any Unicode word<\/del> alphabetic character, you can use
| |
| |
| |