See the ES2022 standard for full specification of the ECMAScript 13 language.
ES13 includes the following new feature proposals:
- Top-level await
- Static class fields and methods
- Static class initialization blocks
- Error.cause
- .at()
- Object: .hasOwn()
In old behavior, When the async/await feature was first introduced, attempting to use an await outside of an async function resulted in a SyntaxError. Many developers utilized as an alternative IIFE (Immediately Invoked Function Expression) as a way to get access to the feature.
Example:
await Promise.resolve(console.log('Hello World'));
// Output: SyntaxError: await is only valid in async function
// Alternative to fix the problem
(async function() {
await Promise.resolve(console.log('Hello World'));
// Output: Hello World
}());
In new behavior, With top-level await, we don't need to use more life hacks.
await Promise.resolve(console.log('Hello World'));
// → Hello World
// 1. Dynamic dependency pathing
const strings = await import(`/i18n/${navigator.language}`);
// This allows for Modules to use runtime values in order to determine dependencies.
// This is useful for things like development/production splits, internationalization, environment splits, etc.
// 2. Resource initialization
const connection = await dbConnector();
// This allows Modules to represent resources and also to produce errors in cases where the Module will never be able to be used.
// 3. Dependency fallbacks
let jQuery;
try {
jQuery = await import('https://cdn-a.com/jQuery');
} catch {
jQuery = await import('https://cdn-b.com/jQuery');
}
Static class fields and methods are not used on instances of a class. Instead, can be called on the class itself and is declared using the static keyword.
Static methods are often utility functions, and helpers, whereas static properties are useful for caches, fixed-configuration, or any other data we don’t need to be replicated across instances.
Example:
// Static class fields
class Test {
static firstName = 'test-static-name';
}
Test.firstName
// Output: test-static-name
// Static class methods
class Test {
static greeting(){
console.log('Hello this is a greeting from a static method');
}
}
Test.greeting();
// Output: Hello this is a greeting from a static method
This new feature provides a mechanism for additional static initialization during class definition evaluation.
Example:
class Test {
static numbers = [1,2,3,4,5,6];
static evenNumbers = [];
static oddNumbers = [];
// static class initialization block
static {
this.numbers.forEach((number) => {
if(!(number % 2) ) {
this.evenNumbers.push(number);
} else {
this.oddNumbers.push(number);
}
});
}
}
Test.evenNumbers;
// Output: [2, 4, 6]
Test.oddNumbers;
// Output: [1, 3, 5]
Error and its subclasses now let us specify the reason behind the error. Sometimes, we catch errors that are thrown during more deeply nested function calls and would like to attach more information to them. With the Error cause, we can add more intrinsic information for our errors. To use this new feature, we should specify the error options as a second parameter, and with the cause key we can pass the error that we want to chain.
Example:
const getUsers = async(array)=> {
try {
const users = await fetch('https://myapi/myusersfake');
return users;
} catch (error) {
console.log('enter')
throw new Error('Something when wrong, please try again later', { cause: error })
}
}
try{
const users = await getUsers();
} catch(error) {
console.log(error); // Error: The array need a minimum of two elements
console.log(error.cause); // TypeError: Failed to fetch
}
Until this point, programmers have asked for the ability to do negative indexing of JS Arrays, as you can do with other programming languages. That is, asking for the ability to write arr[-1] instead of arr[arr.length-1], where negative numbers count backward from the last element.
.at() helps with the negative indexing, the .at() method that is supported by Array, String, or TypedArray.
Example:
// The old behavior
const fruitsArray = ['banana', 'apple', 'orange', 'kiwi'];
console.log(fruitsArray[fruitsArray.length -1])
// Output: kiwi
const fruit = 'kiwi';
console.log(fruit[fruit.length -1])
// Output: i
// The new behavior
const fruitsArray = ['banana', 'apple', 'orange', 'kiwi'];
console.log(fruitsArray.at(-1))
// Output: kiwi
console.log(fruitsArray.at(1))
// Output: apple
const fruit = 'kiwi';
console.log(fruit.at(-1))
// Output: i
Today, it is very common (especially in library code) to write code like
let hasOwnProperty = Object.prototype.hasOwnProperty
if (hasOwnProperty.call(object, "foo")) {
console.log("has property foo")
}
This new feature simplifies that code to
if (Object.hasOwn(object, "foo")) {
console.log("has property foo")
}