Ever since ES2015 released, which was a great leap forward in itself, JavaScript has been improving at a fast pace. Every year since, there have been new features added to the JavaScript specification. Features have been consistently added, like new syntax and new methods for built in JavaScript.
In ES2016 and 2017, the Object
object has had methods like Object.values
and Object.entries
added. String methods like padStart
and padEnd
were added in ES2017. async
and await
, a shorthand syntax for chaining promises, were also added in ES2017. The includes
methods for arrays were added in ES2016. ES2018 was another release with lots of new features. With ES2018, the spread syntax is now available for object literals. Rest parameters were also added. The for await...of
loop, which is a loop syntax iterating through promises sequentially, was added. The SharedArrayBuffer
object for representing raw binary data that cannot become detached was added. A finally
function was also added to the Promise
object.
In 2019, even more new JavaScript features have been added. 2019’s release brings more new methods to arrays and strings. The catch
clause no longer needs to have a binding added to it — that is, the variable enclosed in parentheses after the catch
keyword is longer required.
The description
method has been added to the Symbol
. The toString
method of the Function
object now preserves all characters of the function, so that it shows like it’s in your code, and now JSON.stringify
will always encode in UTF-8.
Array.flat()
The Array.flat()
function converts items in nested arrays to items in top level of the array. It does this recursively up to the given depth. To call the flat
function on an array, we just have to path in the depth of the level that you want to flatten up to, which defaults to 1. It returns a new array with the sub-array elements concatenated into it. To flatten all levels of nested arrays, we can pass in Infinity
.
For example, we can write:
const arr1 = [5, 6, [7, 8]];
console.log(arr1.flat());
// [5, 6, 7, 8]
const arr2 = [5, 6, [7, 8, [9, 10]]];
console.log(arr2.flat());
// [5, 6, 7, 8, [9, 10]]
const arr3 = [5, 6, [7, 8, [9, 10]]];
console.log(arr3.flat(2));
// [5, 6, 7, 8, 9, 10]
const arr4 = [6, 7, [8, 9, [10, 11, [12, 13, [14, 15]]]]];
console.log(arr4.flat(Infinity));
// [6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
As you can see, this is a very handy method for removing nesting from a nested array. This is very valuable since it’s hard work to write our own function to recursively flatten nested arrays into a flattened array less nesting or without any nesting.
Empty slots in arrays are removed after Array.flat
is ran. For example:
const arr = [1, 2, , 3, 4];
arr.flat();
// [1, 2, 3, 4]
The empty slot is removed from the arr
.
Array.flatMap()
The flatMap
method maps an array’s entries into another array and then flatten the results. It’s equivalent to calling map
on an array followed by flat
with depth 1. It’s a great shorthand for mapping and then flattening which has some use cases since lots of arrays aren’t nested too deeply. The flatMap
function take callback function on how to map the array. The callback function takes the current value of the array, index, and the originally array that the flatMap
method is called on as parameters.
For example, we can use flatMap
as in the following examples:
let arr = [1, 2, 3];
arr.map(x => [x * 10]);
// [[10], [20], [30]]]
arr.flatMap(x => [x * 10]);
// [10, 20, 30]
arr.flatMap(x => [[x * 2]]);
// [[2], [4], [6]]
As you can see, flatMap
only flattens one level of the array. If we want to flatten more than one level after mapping we have to call map
and flat
separately.
Function.toString() Changes
Function objects always have the toString()
method to get the string representation of a function. In ES2019, the toString()
function now preserves comments and whitespaces to get the exact string representation of the function as we defined it.
For example, if we have the following:
function fn() {
/* comment */
}
console.log(fn.toString())
We would now get the exactly the same thing as the code if we run toString()
on the function. This means that this is logged:
function fn() {
/* comment */
}
JSON.stringify() Fix
JSON.stringify
now returns the correct characters for special unicode characters since now all characters are now UTF-8 encoded. The characters affected range from code U+D800 to U+DFF.
Now, if we call JSON.stringify
on an object with those characters, we no longer get malformed characters back. If we call JSON.stringify
then call JSON.parse
on the string returned by JSON.parse
, we get the same character back before we stringify it.
String.trimStart()
The string object now has a trimStart()
function to trim the beginning whitespace of a string. There’s also a trimLeft()
method which is an alias to this method.
For example, we can write:
let message = ' Hi! How's it going? ';
console.log(message);
// We get ' Hi! How's it going? '
let message = ' Hi! How's it going? ';
console.log(message.trimStart());
// We get 'Hi! How's it going? '
As we can see, the whitespace on the left side is gone. We can do the same thing with trimLeft()
:
let message = ' Hi! How's it going? ';
console.log(message.trimLeft());
// We get 'Hi! How's it going? '
Note that a new string if returned with trimStart
or trimLeft
, so the original string stays intact. This prevents us from mutating the original string, which is handy for preventing mistakes from accidentally mutating objects.
String.trimEnd()
The trimEnd
method removes whitespace from the right end of the string. trimRight
is an alias of the trimEnd
method. For example, we write:
let message = ' Hi! How's it going? ';
console.log(message);
// We get ' Hi! How's it going? '
let message = ' Hi! How's it going?';
console.log(message.trimEnd());
// We get ' Hi! How's it going?'
Note that a new string if returned with trimEnd
or trimRight
, so the original string stays intact. This prevents us from mutating the original string, which is handy for preventing mistakes from accidentally mutating objects.
Optional Catch Binding
With ES2019, the catch
clause does not have to have a binding added to it. That is, the variable enclosed in parentheses after the catch
keyword is longer required. This means that now we can write this:
try {
···
} catch {
···
}
Now we do not have to write this:
try {
···
} catch (error) {
···
}
This means that if we don’t have to use the error
object, we don’t have to add the binding into the catch
clause. It’s useful for ignoring the error, or if we don’t care what the error is. However, the error
object is still handy for logging and checking for property inputs — and handling errors gracefully will prevent exceptions from being thrown.
For example, we can use it to catch any kind of exception, like with JSON.parse
:
try {
return JSON.parse(str);
} catch {
return {}
}
We can handle errors with invalid JSON strings gracefully. The only issue with that is that we are just swallowing all exceptions, so it might be a better idea to catch some kinds of errors.
We can also use it to use browser features that aren’t supported in all browsers. For instance:
try {
navigator.geolocation
} catch {
return false;
}
Symbol.description
Symbols is a primitive data type that has its own type. There are some static properties and methods of its own that expose the global symbol registry. It’s like a built-in object, but it doesn’t have a constructor, so we can’t write new Symbol
to construct a Symbol object with the new
keyword. It’s mainly used for unique identifiers in an object. It’s a Symbol’s only purpose.
To create new symbols, we can write this:
const fooSymbol = Symbol('foo')
Note that each time we call the Symbol
function, we get a new Symbol — this expression would be false:
Symbol('sym') === Symbol('sym')
With ES2019, we have a description
property which is read only. It returns a string that has the parameter that we pass into the Symbol
function or the object property path of well-known symbols.
If we use the example above and use the description
property, we get this:
const fooSymbol = Symbol('foo');
console.log(fooSymbol.description);
We get foo
logged.
With ES2019, we get more new features that we can use for developing JavaScript apps.
2019’s release brings more new methods to arrays and strings. The catch
clause no longer has to have a binding added to it. That is, the variable enclosed in parentheses after the catch
keyword is longer required.
The descrption
method has been added to the Symbol
.
The toString
method of the Function
object now preserves all characters of the function so that it shows like it’s in your code.
JSON.stringify
will always encode in UTF-8, so now we don’t have malformed characters when we try to stringify characters ranging from code U+D800 to U+DFF.
Finally, arrays now can be flattened without writing our own code with the flat
and flatMap
methods.