Despite the fact that in the last seven years I write in JavaScript almost every working day, I must admit that I pay little attention to news about innovations from ES. The main features like async / await and proxies are one thing, but every year there is a stream of small phased changes that do not fall into my field of vision, since there is always something more important to study.
In this article, I have gathered the capabilities of modern JS, which were little talked about when they appeared. Some of them only increase usability, and some are incredibly practical and can save you writing a bunch of code.
ES2015
Binary and octal literals
JavaScript often does not have to use binary manipulation. But sometimes tasks arise that cannot be otherwise solved. For example, when you write high-performance code for weak devices, you squeeze bits into local storage, perform RGB pixel manipulations in a browser, or work with closely packed binary data formats.
All this may require a lot of work on hiding / combining binary numbers; it always seemed to me that they were in vain hidden in decimals. For such cases, the binary literal format was added to ES6:
0b
.
const binaryZero = 0b0; const binaryOne = 0b1; const binary255 = 0b11111111; const binaryLong = 0b111101011101101;
This greatly simplifies working with binary flags:
// Pizza toppings const olives = 0b0001; const ham = 0b0010; const pineapple = 0b0100; const artechoke = 0b1000; const pizza_ham_pineapple = pineapple | ham; const pizza_four_seasons = olives | ham | artechoke;
Same thing with octal numbers. In the JS world, this is a niche opportunity, but they are often used for networking and some file formats. You can write octal numbers using the
0o
syntax.
Number.isNaN ()
Not to be confused with
window.isNaN()
, this is a new method with much more intuitive behavior.
The classic
isNaN
has some interesting tricks:
isNaN(NaN) === true isNaN(null) === false isNaN(undefined) === true isNaN({}) === true isNaN('0/0') === true isNaN('hello') === true
What does this give us? First, none of these parameters is actually
NaN
. As usual, the problem is with all the “favorite” JavaScript properties: type casting. The arguments for
window.isNaN
are cast to numbers using the
Number
function.
The new static
Number.isNaN()
method solves this problem. He once and for all returns the equality of the arguments passed to him and
NaN
. This is absolutely clear:
Number.isNaN(NaN) === true Number.isNaN(null) === false Number.isNaN(undefined) === false Number.isNaN({}) === false Number.isNaN('0/0') === false Number.isNaN('hello') === false
Signature:
Number.isNaN : (value: any) => boolean
ES2016
Exponentiation operator
This happens from time to time, so it’s good to have literal syntax on hand for exponentiation:
2**2 === 4 3**2 === 9 3**3 === 27
Strange, but I was sure that this already exists in JavaScript. Perhaps confused with Python.
Array.prototype.includes ()
It was hard to miss, but if you wrote
array.indexOf(x) !== -1
in the last three years, then rejoice at the new
includes
method:
[1, 2, 3].includes(2) === true [1, 2, 3].includes(true) === false
includes
uses the Same Value Zero algorithm , which is almost identical to strict equality checking (
===
), except that it can handle
NaN
values. This algorithm also compares objects by links rather than contents:
const object1 = {}; const object2 = {}; const array = [object1, 78, NaN]; array.includes(object1) === true array.includes(object2) === false array.includes(NaN) === true
includes
can take a second parameter,
fromIndex
, which allows you to provide a shift value:
// positions 0 1 2 3 4 const array = [1, 1, 1, 2, 2]; array.includes(1, 2) === true array.includes(1, 3) === false
Useful
Signature:
Array.prototype.includes : (match: any, offset?: Int) => boolean
ES2017
Shared memory and atomic operations
These are a couple of great features that are simply invaluable if you need to do a lot of work with web workers. You can directly share memory with multiple processes and set locks to avoid race conditions.
These are two great features with fairly complex APIs, so I won’t describe them here. For details, send you to this article: https://www.sitepen.com/blog/the-return-of-sharedarraybuffers-and-atomics/ . Not all browsers support these features yet, but I hope that the situation will improve in the next couple of years.
ES2018
Gold mine of regular expressions
ES2018 has a whole bunch of new regex features:
Lookbehind matches (matching with previous characters)
In runtimes that support this, you can now write regular expressions that look for characters before you match. For example, to find all the numbers preceded by a dollar sign:
const regex = /(?<=\$)\d+/; const text = 'This cost $400'; text.match(regex) === ['400']
It's all about the new lookbehind group, the twin lookahead groups:
Look ahead: (?=abc) Look behind: (?<=abc) Look ahead negative: (?!abc) Look behind negative: (?<!abc)
Unfortunately, today you cannot translate the new lookbehind syntax to older browsers, so it’s quite possible that you can only use it in Node for a while.
Named Capture Groups
Regular expressions can now select subsamples and use for simple parsing. Until recently, we could refer to such fragments only by numbers, for example:
const getNameParts = /(\w+)\s+(\w+)/g; const name = "Weyland Smithers"; const subMatches = getNameParts.exec(name); subMatches[1] === 'Weyland' subMatches[2] === 'Smithers'
And now there is the syntax for naming these subsamples (or record groups): inside the brackets at the beginning we put
?<titl>
, if we want to give the group a name:
const getNameParts = /(?<first>\w+)\s(?<last>\w+)/g; const name = "Weyland Smithers"; const subMatches = getNameParts.exec(name); const {first, last} = subMatches.groups first === 'Weyland' last === 'Smithers'
Unfortunately, this only works on Chrome and Node now.
Dots can now mark new lines
It is only necessary to affix the
/s
flag, for example,
/someRegex./s
,
/anotherRegex./sg
.
ES2019
Array.prototype.flat () and flatMap ()
I was very happy to see this on MDN.
Simply put,
flat()
converts a multidimensional array to a one-dimensional array at a given maximum depth:
const multiDimensional = [ [1, 2, 3], [4, 5, 6], [7,[8,9]] ]; multiDimensional.flat(2) === [1, 2, 3, 4, 5, 6, 7, 8, 9]
flatMap
is a
map
followed by
flat
with a depth of 1. This is useful if you need to map a function that returns an array, but you don’t need the result to be a nested data structure:
const texts = ["Hello,", "today I", "will", "use FlatMap"]; // with a plain map const mapped = texts.map(text => text.split(' ')); mapped === ['Hello', ['today', 'I'], 'will', ['use', 'FlatMap']]; // with flatmap const flatMapped = texts.flatMap(text => text.split(' ')); flatMapped === ['Hello', 'today', 'I', 'will', 'use', 'FlatMap'];
Unlimited Catch
Now you can write try / catch expressions without reference to throwing errors:
try { // something throws } catch { // don't have to do catch(e) }
By the way, hooks in which you do not take into account the value of
e
are sometimes referred to as handling Pokémon exceptions . Because you have to catch them all!
String value trimming methods
Slightly, but nice:
const padded = ' Hello world '; padded.trimStart() === 'Hello world '; padded.trimEnd() === ' Hello world';