JavaScript 2022 (ES13)
Top-level await, class fields, and more powerful features
✨ What's New in JavaScript 2022?
JavaScript 2022 (ES13) brought game-changing features like top-level await, class fields, and the at() method. These features make JavaScript more powerful and developer-friendly.
// New in 2022: Top-level await (no need for async function wrapper)
const data = await fetch('/api/users');
const users = await data.json();
console.log(users);
Key Features of ES13
Top-level Await
Use await outside async functions
// No async wrapper needed!
const data = await fetchData();
console.log(data);
Class Fields
Define fields directly in class body
class User {
name = 'Anonymous';
#id = Math.random();
}
Array.at() Method
Access array elements with negative indices
const arr = [1, 2, 3, 4, 5];
console.log(arr.at(-1)); // 5
console.log(arr.at(-2)); // 4
Object.hasOwn()
Better way to check object properties
const obj = { name: 'John' };
Object.hasOwn(obj, 'name'); // true
🔹 Top-level Await
Use await directly in modules without wrapping in async functions:
// Before ES13 - needed async wrapper
(async () => {
const response = await fetch('/api/config');
const config = await response.json();
console.log(config);
})();
// ES13 - direct top-level await in modules
const response = await fetch('/api/config');
const config = await response.json();
// Use the config immediately
document.title = config.appName;
// Multiple awaits work too
const userResponse = await fetch('/api/user');
const user = await userResponse.json();
const settingsResponse = await fetch(`/api/settings/${user.id}`);
const settings = await settingsResponse.json();
console.log('App initialized with user:', user.name);
Output:
App initialized with user: John Doe
🔹 Class Fields & Static Blocks
Define class properties and initialization logic more elegantly:
class GameCharacter {
// Public fields (no need for constructor)
name = 'Unknown';
level = 1;
health = 100;
// Private fields
#experience = 0;
#maxHealth = 100;
// Static fields
static totalCharacters = 0;
static #gameVersion = '2.1.0';
// Static initialization block
static {
console.log(`Game version: ${this.#gameVersion}`);
this.totalCharacters = 0;
}
constructor(name) {
this.name = name;
GameCharacter.totalCharacters++;
}
// Private method
#calculateLevelUp() {
return Math.floor(this.#experience / 100);
}
gainExperience(points) {
this.#experience += points;
const newLevel = this.#calculateLevelUp();
if (newLevel > this.level) {
this.level = newLevel;
this.health = this.#maxHealth; // Full heal on level up
console.log(`${this.name} leveled up to ${this.level}!`);
}
}
getStats() {
return {
name: this.name,
level: this.level,
health: this.health,
experience: this.#experience
};
}
}
// Usage
const hero = new GameCharacter('Aragorn');
hero.gainExperience(150);
console.log(hero.getStats());
console.log(`Total characters: ${GameCharacter.totalCharacters}`);
Output:
Game version: 2.1.0
Aragorn leveled up to 1!
{ name: 'Aragorn', level: 1, health: 100, experience: 150 }
Total characters: 1
🔹 Array.at() Method
Access array elements with negative indices (like Python!):
const fruits = ['apple', 'banana', 'cherry', 'date', 'elderberry'];
// Old way to get last element
console.log(fruits[fruits.length - 1]); // 'elderberry'
// New way with at() - much cleaner!
console.log(fruits.at(-1)); // 'elderberry'
console.log(fruits.at(-2)); // 'date'
console.log(fruits.at(-3)); // 'cherry'
// Works with positive indices too
console.log(fruits.at(0)); // 'apple'
console.log(fruits.at(1)); // 'banana'
// Useful for dynamic access
function getLastN(array, n) {
return array.at(-n);
}
console.log(getLastN(fruits, 1)); // 'elderberry'
console.log(getLastN(fruits, 3)); // 'cherry'
// Works with strings too!
const text = 'Hello World';
console.log(text.at(-1)); // 'd'
console.log(text.at(-6)); // 'W'
Output:
elderberry date cherry apple banana elderberry cherry d W
🔹 Object.hasOwn()
A safer way to check if an object has its own property:
const user = {
name: 'Alice',
age: 30,
email: '[email protected]'
};
// Old way - can be problematic
console.log(user.hasOwnProperty('name')); // true
console.log('name' in user); // true (but includes inherited properties)
// New way - Object.hasOwn() is safer
console.log(Object.hasOwn(user, 'name')); // true
console.log(Object.hasOwn(user, 'age')); // true
console.log(Object.hasOwn(user, 'toString')); // false (inherited method)
// Why Object.hasOwn() is better:
// 1. Works even if hasOwnProperty is overridden
const problematicObj = {
name: 'test',
hasOwnProperty: null // Oops! Method is overridden
};
// This would throw an error:
// console.log(problematicObj.hasOwnProperty('name')); // TypeError
// This works fine:
console.log(Object.hasOwn(problematicObj, 'name')); // true
// 2. Works with objects created with null prototype
const nullProtoObj = Object.create(null);
nullProtoObj.prop = 'value';
// This would throw an error:
// console.log(nullProtoObj.hasOwnProperty('prop')); // TypeError
// This works:
console.log(Object.hasOwn(nullProtoObj, 'prop')); // true
Output:
true true true true false true true
🔹 RegExp Match Indices
Get the start and end positions of regex matches:
const text = 'The quick brown fox jumps over the lazy dog';
const regex = /quick|fox|dog/g;
// Add 'd' flag to get indices
const regexWithIndices = /quick|fox|dog/gd;
// Regular match
const matches = [...text.matchAll(regex)];
console.log('Regular matches:', matches[0][0]); // 'quick'
// Match with indices
const matchesWithIndices = [...text.matchAll(regexWithIndices)];
console.log('Match:', matchesWithIndices[0][0]); // 'quick'
console.log('Indices:', matchesWithIndices[0].indices[0]); // [4, 9]
// Practical example: highlighting matches
function highlightMatches(text, pattern) {
const regex = new RegExp(pattern, 'gdi');
const matches = [...text.matchAll(regex)];
let result = text;
let offset = 0;
matches.forEach(match => {
const [start, end] = match.indices[0];
const before = result.slice(0, start + offset);
const matched = result.slice(start + offset, end + offset);
const after = result.slice(end + offset);
result = before + `${matched}` + after;
offset += 13; // Length of ''
});
return result;
}
console.log(highlightMatches(text, 'quick|fox|dog'));
Output:
Regular matches: quick Match: quick Indices: [4, 9] The quick brown fox jumps over the lazy dog