Proposal • MDN • v8.dev
Using the regex d
flag, additionally return the start and end indices for individual capture groups on regex matches.
/(?<x>a)(?<y>b)/d.exec('ab')
// ['ab', 'a', 'b']
/(?<x>a)(?<y>b)/d.exec('ab').indices
// [[0, 2], [0, 1], [1, 2]]
/(?<x>a)(?<y>b)/d.exec('ab').indices.groups
// { x: [0, 1], y: [1, 2] }
✅ Chrome - Since v90
✅ Firefox - Since v89
🟡 Safari - Since v15? (not mentioned in release notes) technical preview 122
✅ Node - Since v16.0.0 (v8 9.0)
CanIUse - unavailable
Proposal • MDN • v8.dev
use await
outside async functions in a module.
await Promise.resolve(console.log('🎉'));
✅ Babel
✅ Typescript - Since v3.8
✅ SWC
✅ Sucrase
✅ Chrome - Since v89
✅ Firefox - Since v89
🟡 Safari - Since v15 technical preview 122
✅ Node - Since v16.4.0 - not in commonjs modules (v8 9.1)
CanIUse
Proposal
Declare fields (this.variable
) outside constructor. Create private fields which
cannot be accessed from outside the class.
class Fields {
x = 0;
#y = 0;
constructor() {
this.x = 0;
this.#y = 1;
}
}
const obj = new Fields();
console.log(obj.x); // 0
console.log(obj.#y); // error
Proposal
Add private methods and accessors (getter/setters).
class Example {
#xValue = 0;
get #x() {
return #xValue;
}
set #x(value) {
this.#xValue = value;
}
#add() {
this.#x = this.#x + 2;
}
constructor() {
this.#add();
}
}
class StaticMethodCall {
static #privateStaticProperty = 'private static property';
static staticProperty = 'static property';
static staticMethod() {
}
static #privateStaticMethod() {
}
}
StaticMethodCall.staticMethod();
Proposal • MDN • v8.dev
Check if a private field exists in an object using the in
operator.
class C {
#brand;
#method() {}
get #getter() {}
static isC(obj) {
return #brand in obj && #method in obj && #getter in obj;
}
}
✅ Babel
⛔ Typescript
⛔ SWC
⛔ Sucrase
✅ Chrome - Since v91
✅ Firefox - Since v90
🟡 Safari - Since v15? (not mentioned in release notes) technical preview 127
✅ Node - Since v16.4.0 (v8 9.1)
CanIUse - Not available
MDN • v8.dev
Allow making numbers more readable by separating it with _
(underscore)
let budget = 1_000_000_000_000;
✅ Babel
✅ Typescript - Since v2.7
✅ SWC
✅ Sucrase
✅ Chrome - Since v75
✅ Firefox - Since v70
✅ Safari - Since v13
✅ Node - Since v12.5.0
CanIUse
MDN • v8.dev
Combine Logical Operators and Assignment Expressions
// "Or Or Equals"
a ||= b;
a || (a = b);
// "And And Equals"
a &&= b;
a && (a = b);
// "QQ Equals"
a ??= b;
a ?? (a = b);
✅ Babel
✅ Typescript - Since v4.0
⛔ SWC
⛔ Sucrase
✅ Chrome - Since v85
✅ Firefox - Since v79
✅ Safari - Since v14
✅ Node - Since v15.0.0
CanIUse
MDN • v8.dev
A WeakRef object lets you hold a weak reference to another object, without preventing that object from getting garbage-collected.
Related: FinalizationRegistry
const ref = new WeakRef(someObject);
// Try to reference the original object
const obj = ref.deref();
if (obj) {
console.log('The obj is available.');
} else {
console.log('The obj has been removed.');
}
✅ Chrome - Since v84
✅ Firefox - Since v79
✅ Safari - Since v14 (iOS Safari v14.7)
✅ Node - Since v14.6.0
CanIUse
Cannot be polyfilled
MDN • v8.dev
Takes a list of Promises (or iterable) and as soon as one of the promises fulfills(resolves), returns a single promise that resolves with the value from that promise. If no promise fulfills (if all of the given promises are rejected), then the returned promise is rejected with an AggregateError.
const promise1 = Promise.reject(0);
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 'quick'));
const promise3 = new Promise((resolve) => setTimeout(resolve, 500, 'slow'));
const promises = [promise1, promise2, promise3];
Promise.any(promises).then((value) => console.log(value));
// output: "quick"
✅ Chrome - Since v89
✅ Firefox - Since v86
✅ Safari - Since v14
✅ Node - Since v15.0.0
CanIUse
Polyfill
MDN • v8.dev
Replace all instances of a substring (literal or regexp) in a string
const queryString = 'q=query+string+parameters';
const withSpaces = queryString.replaceAll('+', ' ');
✅ Chrome - Since v85
✅ Firefox - Since v77
✅ Safari - Since v13.1
✅ Node - Since v15.0.0
CanIUse
Polyfill
MDN • v8.dev
The import.meta
is a special object which contains metadata of the currently running JavaScript module.
import
is not an object, only import.meta
is an object. This object is mutable and can be used to store arbitrary information.
Example usage in deno.
Example usage in vite.
// Load a module
<script type="module" src="my-module.js"></script>
// my-module.js
console.log(import.meta); // { url: "<url>" }
✅ Chrome - Since v64
✅ Firefox - Since v62
✅ Safari - Since v11.1 (iOS Safari v12)
✅ Node - Since v10.4.0
CanIUse
MDN • v8.dev
The nullish coalescing operator ??
is a logical operator that returns its right-hand side operand when its left-hand side operand is null or undefined, and otherwise returns its left-hand side operand.
let x = foo ?? bar();
// Which is equivalent to
let x = (foo !== null && foo !== undefined) ? foo : bar();
✅ Babel
✅ Typescript - Since v3.7
✅ SWC
✅ Sucrase
✅ Chrome - Since v80
✅ Firefox - Since v72
✅ Safari - Since v13.1 (iOS Safari v13.7)
✅ Node - Since v14.0.0
CanIUse
MDN • v8.dev
The ?.
operator functions similarly to the .
chaining operator, except that instead of causing an error if a reference is nullish (null or undefined), the expression short-circuits with a return value of undefined.
let x = foo?.bar.baz();
// Which is equivalent to
let x = (foo === null || foo === undefined) ? undefined : foo.bar.baz();
// Calling an optional method
myForm.checkValidity?.()
✅ Babel
✅ Typescript - Since v3.7
✅ SWC
✅ Sucrase
✅ Chrome - Since v80
✅ Firefox - Since v72
✅ Safari - Since v13.1 (iOS Safari v13.7)
✅ Node - Since v14.0.0
CanIUse
Proposal
Standardize the order of for-in loops.
MDN • v8.dev
Standard way to access the global object in all JavaScript environments (window in Browser, global in Node).
function canMakeHTTPRequest() {
return typeof globalThis.XMLHttpRequest === 'function';
}
✅ Typescript lib - Since v3.4
✅ Chrome - Since v71
✅ Firefox - Since v65
✅ Safari - Since v12.1 (iOS Safari v12.2)
✅ Node - Since v12.0.0
Polyfill
CanIUse
MDN • v8.dev
Promise.allSettled
is a new Promise method that returns a Promise that is fulfilled when all of the input promises are fulfilled or rejected.
const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
const results = await Promise.allSettled(promises);
const successfulPromises = results.filter(p => p.status === 'fulfilled');
✅ Typescript lib - Since v3.8
✅ Chrome - Since v76
✅ Firefox - Since v71
✅ Safari - Since v13
✅ Node - Since v12.0.0
Polyfill
CanIUse
MDN • v8.dev
The BigInt
type is a new numeric primitive in ECMAScript, which is a signed integer type.
BigInt would dynamically resize memory to fit the actual value.
The maximum size of BigInt is unspecified and left to the implementation.
https://stackoverflow.com/a/54298760
tc39/proposal-bigint#174
https://v8.dev/blog/bigint
const huge = BigInt(9007199254740991)
// 9007199254740991n
// BigInt numbers end with a "n".
✅ Typescript lib - Since v3.8
✅ Chrome - Since v67
✅ Firefox - Since v68
✅ Safari - Since v14 (iOS Safari v14.4)
✅ Node - Since v10.4.0
CanIUse
Cannot be polyfilled
MDN • v8.dev
Dynamic imports allows you to import modules at run-time.
import('/modules/my-module.js')
.then(module => {
module.loadPageInto(main);
})
.catch(err => {
main.textContent = err.message;
});
✅ Babel
✅ Typescript - Since v2.4
✅ SWC
✅ Sucrase
✅ Chrome - Since v63
✅ Firefox - Since v67
✅ Safari - Since v11.1 (iOS Safari v11.3)
✅ Node - Since v13.2.0, later enabled in v12.17.0
CanIUse
MDN • v8.dev
matchAll
returns a RegExpStringIterator of matches matching a string or regex also with capture groups.
const regexp = /t(e)(st(\d?))/g;
const str = 'test1test2';
const match = str.matchAll(regexp);
for (const m of str.matchAll(regexp)) {
console.log(m);
}
// ["test1", "e", "st1", "1"]
// ["test2", "e", "st2", "2"]
✅ Typescript lib - Since v3.8
✅ Chrome - Since v63
✅ Firefox - Since v67
✅ Safari - Since v11.1 (iOS Safari v11.3)
✅ Node - Since v13.2.0, later enabled in v12.17.0
CanIUse
Proposal • MDN-flat • MDN-flatMap • v8.dev
flat
- Create a new array with sub-array elements concatenated into it recursively up to the specified depth.
flatMap
- Map over and flatten an array (to 1 level). Equivalent to running Array.prototype.map
then Array.prototype.flat
with depth 1.
const arr2 = [0, 1, 2, [[[3, 4]]]];
console.log(arr2.flat(3));
// [0, 1, 2, 3, 4]
let arr = [[1, 3], [4, 6], [7, 9], [10, 12]];
console.log(arr.flatMap(x => [x[0], (x[0] + x[1]) / 2, x[1]]));
// [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ]
Proposal • MDN-trimStart • MDN-trimEnd • v8.dev
const string = ' hello world ';
string.trimStart();
// 'hello world '
string.trimEnd();
// ' hello world'
CanIUse - trimStart
CanIUse - trimEnd
Proposal • MDN • v8.dev
Stringify lone surrogates i.e. any code point from U+D800 to U+DFFF, using Unicode escape sequences.
Before this change JSON.stringify would output lone surrogates if the input contained any lone surrogates; such strings could not be encoded in valid UTF-8 or UTF-16:
// Before
JSON.stringify("\uD800"); // '"�"'
// After
JSON.stringify("\uD800"); // '"\\ud800"'
Proposal • MDN • v8.dev
It accepts an iterable of key-value pairs and returns a new object whose own keys and corresponding values are given by those pairs.
obj = Object.fromEntries([['a', 0], ['b', 1]]);
// { a: 0, b: 1 }
const symbol = Symbol('foo');
symbol.description;
// 'foo'
Proposal • MDN • v8.dev
JSON becomes a syntactic subset of ECMAScript. Before this, ECMAScript string literals couldn’t contain unescaped U+2028 LINE SEPARATOR and U+2029 PARAGRAPH SEPARATOR characters which JSON could have.
try {
doSomethingThatMightThrow();
} catch { // catch parameter is now optional
handleException();
}