DEV Community

Cover image for Javascript features you might not know in 2019-2020
Deepak Gupta
Deepak Gupta

Posted on • Originally published at overflowjs.com

Javascript features you might not know in 2019-2020

Private Class field👇

Until ES6, we were not able to declare private properties directly. Yes there were ways like underscore convention (_propertyName), closures, symbols, or WeakMaps.

But now private class fields use a hash # prefix. Let’s learn it by an example.

class Test {
  a = 1;          // .a is public
  #b = 2;         // .#b is private
  static #c = 3;  // .#c is private and static
  incB() {
    this.#b++;
  }
}
const testInstance = new Test();
// runs OK
testInstance.incB();
// error - private property cannot be modified outside class
testInstance.#b = 0;

Note: There’s no way to define the private function as of now, although a TC39 stage 3: draft proposal suggests using a hash # prefix on names. 🤞

String.matchAll()👇

If I have a string, with a global regular expression which has many capturing groups, I often want to iterate through all groups. Currently, my options are the following:

  1. String.prototype.match() with /g— If we use .match() with a regular expression whose flag /g is set, you get all full matches for it in an Array.

  2. String.prototype.split() — If we use a split string and regular expression to specify the separator and if it contains at least one capture group then .split() returns an Array in which the substrings are interleaved.

The issues with the above approach are that they only work if /g is set on regular expression and the property .lastIndex of a regular expression is changed each time a match happens. This makes using the same regular expression at multiple locations risky.

The matchAll() help resolve all above. Let’s check out the definition and usage

Given a string and a regular expression, .matchAll() returns all results matching a string against a regular expression, including capturing groups.

let regexp = /t(e)(st(\d?))/g;
let str = 'test1test2';
let array = [...str.matchAll(regexp)];
console.log(array[0]);
// expected output: Array ["test1", "e", "st1", "1"]

Note: .matchAll() returns an iterator, not a true restartable iterable. That is, once the result is exhausted, you need to call the method again and create a new iterator.

Numeric Separators👇

If you have struggled to read a long sequence of number, this is where your search end.

Numeric Separators allow the human eye to parse quickly, especially when there are lots of repeating digits:

1000000000000 -> 1_000_000_000_000
1019436871.42 -> 1_019_436_871.42

Now it’s easier to tell that the first number is a trillion, and the second number is in the order of 1 billion.

It also works on other bases, for example:

const fileSystemPermission = 0b111_111_000;
const bytes = 0b1111_10101011_11110000_00001101;
const words = 0xFAB_F00D;

You can also use the separator in the fractions and exponents:

const massOfElectronInKg = 9.109_383_56e-31;
const trillionInShortScale = 1e1_2;

Note: Parsing the _ separated integer can be tricky as Number('123_456') gives NAN whereas parseInt('123_456') gives 123.

BigInt’s👇

BigInts are a new numeric primitive in JavaScript that can represent integers with precision larger than 2⁵³–1. With BigInts, you can safely store and operate on large integers even beyond the safe integer limit for Numbers.

BigInts correctly perform integer arithmetic without overflowing. Let’s understand by an example:-

const max = Number.MAX_SAFE_INTEGER;
// 9007199254740991
max+1;
// 9007199254740992
max+2;
// 9007199254740991

We can see that max + 1 produces the same result as max + 2 .

Any calculation on integers outside the safe integer range (i.e. from Number.MIN_SAFE_INTEGER to Number.MAX_SAFE_INTEGER) potentially loses precision. For this reason, we can only rely on numeric integer values within the safe range.

Therefore, BigInts came to existence, BigInts can be created by adding the n suffix to any integer literal. For example, 123 becomes 123n or the global BigInt(number) function can be used to convert a Number into a BigInts.

Let’s revisit the above example with BigInts

BigInt(Number.MAX_SAFE_INTEGER) + 2n;
// 9007199254740993n
typeof 123n
// "bigint2"

Note: Numeric separators are especially helpful with BigInts, for eg:

const massOfEarthInKg = 6_000_000_000_000_000_000_000_000n;

BigInts support the most common operators. Binary +, -, , and * all work as expected. / and % work, and round towards zero as needed.

(7 + 6 - 5) * 4 ** 3 / 2 % 3;
// → 1
(7n + 6n - 5n) * 4n ** 3n / 2n % 3n;
// → 1n

Note: One gotcha is that it’s not allowed to mix operations between BigInts and Numbers

Locale String with BigInt👇

The toLocaleString() method returns a string with a language-sensitive representation of the BigInt.

let bigint = 123456789123456789n;

// German uses period for thousands
console.log(bigint.toLocaleString('de-DE'));
// → 123.456.789.123.456.789

// Arabic in most Arabic speaking countries uses Eastern Arabic digits
console.log(bigint.toLocaleString('ar-EG'));
// → ١٢٣٬٤٥٦٬٧٨٩٬١٢٣٬٤٥٦٬٧٨٩

// India uses thousands/lakh/crore separators
console.log(bigint.toLocaleString('en-IN'));
// → 1,23,45,67,89,12,34,56,789

// the nu extension key requests a numbering system, e.g. Chinese decimal
console.log(bigint.toLocaleString('zh-Hans-CN-u-nu-hanidec'));
// → 一二三,四五六,七八九,一二三,四五六,七八九

// when requesting a language that may not be supported, such as
// Balinese, include a fallback language, in this case Indonesian
console.log(bigint.toLocaleString(['ban', 'id']));
// → 123.456.789.123.456.789

GlobalThis Keyword👇

JavaScript’s variable scopes are nested and form a tree whose root is the global scope and the value of this keyword is a reference to the object that “owns” the currently executing code or the function where its looked at.

To read more about this keyword and global scope read my below articles

  1. Scopes in Javascript

  2. Understanding Javascript ‘this’ keyword (Context)

Usually to figure out the global this we use a function like

const getGlobalThis = () => {

  // in webworker or service worker
  if (typeof self !== 'undefined') return self;

  // in browser 
  if (typeof window !== 'undefined') return window;

  // in Node.js
  if (typeof global !== 'undefined') return global;

  // Standalone javascript shell
  if (typeof this !== 'undefined') return this;

  throw new Error('Unable to locate global object');
};
const theGlobalThis = getGlobalThis();

The above function does not cover all cases when we need global this value.

In the case of use strict the value of this is undefined.

When we form a bundle in the javascript it usually wraps under some code that might differ the global this.

In Standalone javascript engine shell environment, the above code will not work.

To solve the above problem globalThis keyword is introduced which returns global this object in any environment at any time.

Note: The global object is now considered a mistake that JavaScript can’t get rid of, due to backward compatibility. It affects performance negatively and is generally confusing.

Promise.allSettled()👇

If you are wondering what’s promise is in javascript then check out this — JavaScript Promises: an Introduction.

A little gist, a promise is JavaScript’s way of promising you that work will be done (or might fail if the work could not be completed).

The new method returns a promise that resolves after all of the given promises have settled i.e either resolved or rejected, with an array of objects that each describe the outcome of each promise.

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];
Promise.allSettled(promises).
  then((results) => results.forEach((result) => console.log(result.status)));
// expected output:
// "fulfilled"
// "rejected"

This is different from the Promise.all as it rejects as soon as a promise within the iterable object rejected.

Below is the comparison of current supported promises method
Alt Text

Dynamic Import 👇

Alt Text

This one is crazy before we dive into it, let’s first see what static import is.

Static import only accepts a string literal as the module specifier and introduces bindings into the local scope via a pre-runtime “linking” process.

The static import syntax can only be used at the top-level of the file.

import * as module from './utils.mjs';

A static import enables important use cases such as static analysis, bundling tools, and tree-shaking.

But what about

  1. import a module on-demand (or conditionally)

  2. compute the module specifier at runtime

  3. import a module from within a regular script (as opposed to a module)

This was not possible until dynamic imports — import(moduleSpecifier) returns a promise for the module namespace object of the requested module, which is created after fetching, instantiating, and evaluating all of the module’s dependencies, as well as the module itself.

<script type="module">
  (async () => {
    const moduleSpecifier = './utils.mjs';
    const module = await import(moduleSpecifier)
    module.default();
    // → logs 'Hi from the default export!'
    module.doStuff();
    // → logs 'Doing stuff…'
  })();
</script>

Note: Use static imports for initial paint dependencies, especially for above-the-fold content. In other cases, consider loading dependencies on-demand with dynamic import().

Stable Sorting — (Consistent and reliable result now)👇

By Stable in the algorithmic sense means: does it preserve the order or otherwise “equal” items?

Let’s understand by an example

const people = [
  {name: 'Gary', age: 20},
  {name: 'Ann', age: 20},
  {name: 'Bob', age: 17},
  {name: 'Sue', age: 21},
  {name: 'Sam', age: 17},
];

// Sort people by name
people.sort( (p1, p2) => {
  if (p1.name < p2.name) return -1;
  if (p1.name > p2.name) return 1;
  return 0;
});

console.log(people.map(p => p.name));
// ['Ann', 'Bob', 'Gary', 'Sam', 'Sue']
// Re-sort people by age

people.sort( (p1, p2) => {
  if (p1.age < p2.age) return -1;
  if (p1.age > p2.age) return 1;
  return 0;
});

console.log(people.map(p => p.name));
// We're expecting people sorted by age, then by name within age group:
// ['Bob', 'Sam', 'Ann', 'Gary', 'Sue']
// But we might get any of these instead, depending on the browser:
// ['Sam', 'Bob', 'Ann', 'Gary', 'Sue']
// ['Bob', 'Sam', 'Gary', 'Ann', 'Sue']
// ['Sam', 'Bob', 'Gary', 'Ann', 'Sue']

If you’re getting one of the last three results, then you’re probably using Google Chrome, or maybe one of an assortment of browsers that do not implement Array.sort() as a “stable” algorithm.

This is because different JS engines (across different browsers) taking different routes to implementing sort, Also, some javascript engine use stable sort for short array but for long array uses unstable sort.

This lead to inconsistent in sort stability behavior and a lot of confusion. This is why in the development environment everything related to sort seems to work but in the production environment, we start to see something else due to the varying size of array the sort was tested on.

Note: There are 3rd party libraries, I heartily recommend Lodash, which has stable sort

But this has been resolved now, we have a stable sort on most of the browsers. The syntax remains the same.

Since this article has a lot to digest and try-test features, we will continue with the more new feature in the next one.

Note: This article was originally published on overflowjs.com

Please consider entering your email here, if you’d like to be added to my email list and follow me on dev.to to read more article on javascript and on GitHub to see my crazy code.

Thank you !

Top comments (6)

Collapse
 
ctrlsquid profile image
zach

Oh man, I'm absolutely stoked for Promise.allSettled and dynamic imports!

Collapse
 
dg92 profile image
Deepak Gupta

Me too :), you can try them in the chrome browser (version ≥76) or Node.js(version ≥11) CLI.

Collapse
 
dhkamp profile image
David Hölkeskamp

Excellent post, thank you!

Collapse
 
dg92 profile image
Deepak Gupta

Thank you! Glad you like it, feel free to share around.

Collapse
 
clarity89 profile image
Alex K. • Edited

Lot's of good news there :)

Btw in your bigInt example the output of max + 1 and max + 2 is different.

Collapse
 
adam_cyclones profile image
Adam Crockett 🌀

Well that about rounds it up. I do love the bigint and tolocalestring demo, it certainly illustrated the (imo) nicest API in JavaScript to date, intl ♥️.