{"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>\n

These 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>\n

const 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>\n

The 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>\n

ES2018 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>\n

In 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>\n

const 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>\n

Spread 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>\n

const 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>\n

Note, however, that spread properties do not always produce the same result as Object.assign()<\/code>. Consider the following code:<\/p>\n

Object.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>\n

In this code, the Object.assign()<\/code> method executes the inherited setter property. Conversely, the spread properties simply ignore the setter.<\/p>\n

It’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>\n

const 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>\n

Inherited 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>\n

In 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>\n

Keep 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>\n

The 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>\n

Another useful feature added to ES2015 was rest parameters, which enabled JavaScript programmers to use ...<\/code> to represent values as an array. For example:<\/p>\n

const arr = [10, 20, 30];\nconst [x, ...rest] = arr;\n\nconsole.log(x);       \/\/ \u2192 10\nconsole.log(rest);    \/\/ \u2192 [20, 30]<\/code><\/pre>\n

Here, 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>\n

const 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>\n

This 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>\n

Also 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>\n

Support for Rest\/Spread Properties<\/h4>\n\n\n\n\n\n
Chrome<\/th>\nFirefox<\/th>\nSafari<\/th>\nEdge<\/th>\n<\/tr>\n<\/thead>\n
60<\/td>\n55<\/td>\n11.1<\/td>\nNo<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n\n\n\n\n\n
Chrome Android<\/th>\nFirefox Android<\/th>\niOS Safari<\/th>\nEdge Mobile<\/th>\nSamsung Internet<\/th>\nAndroid Webview<\/th>\n<\/tr>\n<\/thead>\n
60<\/td>\n55<\/td>\n11.3<\/td>\nNo<\/td>\n8.2<\/td>\n60<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n

Node.js:<\/strong><\/strong><\/p>\n