JavaScript ES2024 : L'évolution continue du langage web
ES2024 (ES15) apporte des améliorations significatives à JavaScript avec des fonctionnalités attendues depuis longtemps par la communauté des développeurs.
1. Array.fromAsync() - Création d'arrays asynchrones
Créez des arrays à partir de sources asynchrones de manière élégante :
// Avant ES2024 - approche complexe
async function createAsyncArray(asyncIterable) {
const result = [];
for await (const item of asyncIterable) {
result.push(await processItem(item));
}
return result;
}
// ES2024 - Array.fromAsync()
async function* asyncGenerator() {
yield Promise.resolve(1);
yield Promise.resolve(2);
yield Promise.resolve(3);
}
const asyncArray = await Array.fromAsync(asyncGenerator());
console.log(asyncArray); // [1, 2, 3]
// Avec transformation
const processedArray = await Array.fromAsync(
asyncGenerator(),
async (x) => x * 2
);
console.log(processedArray); // [2, 4, 6]
// Utilisation avec des APIs
const apiUrls = [
'https://api.example1.com/data',
'https://api.example2.com/data',
'https://api.example3.com/data'
];
const responses = await Array.fromAsync(
apiUrls,
async (url) => {
const response = await fetch(url);
return response.json();
}
);
2. Object.groupBy() - Groupement d'objets natif
const users = [
{ name: 'Alice', department: 'Engineering', salary: 75000 },
{ name: 'Bob', department: 'Engineering', salary: 80000 },
{ name: 'Charlie', department: 'Marketing', salary: 65000 },
{ name: 'Diana', department: 'Marketing', salary: 70000 },
{ name: 'Eve', department: 'HR', salary: 60000 }
];
// ES2024 - Object.groupBy()
const groupedByDepartment = Object.groupBy(users, (user) => user.department);
console.log(groupedByDepartment);
/*
{
Engineering: [
{ name: 'Alice', department: 'Engineering', salary: 75000 },
{ name: 'Bob', department: 'Engineering', salary: 80000 }
],
Marketing: [
{ name: 'Charlie', department: 'Marketing', salary: 65000 },
{ name: 'Diana', department: 'Marketing', salary: 70000 }
],
HR: [
{ name: 'Eve', department: 'HR', salary: 60000 }
]
}
*/
// Groupement par ranges de salaire
const groupedBySalary = Object.groupBy(users, (user) => {
if (user.salary < 65000) return 'Low';
if (user.salary < 75000) return 'Medium';
return 'High';
});
// Map.groupBy() pour des clés non-string
const usersByFirstLetter = Map.groupBy(users, (user) => user.name[0]);
3. Promise.withResolvers() - Contrôle manuel des Promises
// ES2024 - Promise.withResolvers()
function createControllablePromise() {
const { promise, resolve, reject } = Promise.withResolvers();
// Vous pouvez maintenant contrôler la promise de l'extérieur
setTimeout(() => {
resolve('Promise resolved after 2 seconds!');
}, 2000);
return { promise, resolve, reject };
}
// Usage pratique - System de cache avec timeout
class CacheManager {
constructor() {
this.cache = new Map();
}
async get(key, fetcher, ttl = 5000) {
if (this.cache.has(key)) {
return this.cache.get(key).promise;
}
const { promise, resolve, reject } = Promise.withResolvers();
this.cache.set(key, { promise, resolve, reject });
// Timeout
const timeoutId = setTimeout(() => {
this.cache.delete(key);
reject(new Error('Cache timeout'));
}, ttl);
try {
const result = await fetcher();
clearTimeout(timeoutId);
resolve(result);
return result;
} catch (error) {
clearTimeout(timeoutId);
this.cache.delete(key);
reject(error);
throw error;
}
}
}
// Usage
const cacheManager = new CacheManager();
const userData = await cacheManager.get(
'user-123',
() => fetch('/api/user/123').then(r => r.json()),
10000
);
4. String.prototype.isWellFormed() et toWellFormed()
// Vérification et correction des strings Unicode
const wellFormedString = "Hello 👋 World";
const malformedString = "Hello \uD83D World"; // Surrogate pair incomplet
console.log(wellFormedString.isWellFormed()); // true
console.log(malformedString.isWellFormed()); // false
// Correction automatique
const corrected = malformedString.toWellFormed();
console.log(corrected); // "Hello � World" (avec caractère de remplacement)
// Utilisation pratique avec des données externes
function sanitizeUserInput(input) {
if (!input.isWellFormed()) {
return input.toWellFormed();
}
return input;
}
// Validation d'API
app.post('/api/comments', (req, res) => {
const comment = req.body.comment;
if (!comment.isWellFormed()) {
return res.status(400).json({
error: 'Invalid Unicode in comment text'
});
}
// Traitement du commentaire...
});
5. ArrayBuffer.prototype.resize() et transfer()
// Création d'un ArrayBuffer redimensionnable
const buffer = new ArrayBuffer(8, { maxByteLength: 16 });
const uint8Array = new Uint8Array(buffer);
// Remplissage initial
uint8Array.set([1, 2, 3, 4, 5, 6, 7, 8]);
console.log(buffer.byteLength); // 8
// Redimensionnement
buffer.resize(12);
console.log(buffer.byteLength); // 12
// Les nouvelles données sont initialisées à 0
const expandedArray = new Uint8Array(buffer);
console.log(expandedArray); // [1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0]
// Transfer - déplacement efficace de la propriété
const transferredBuffer = buffer.transfer(16);
console.log(buffer.byteLength); // 0 (buffer original détaché)
console.log(transferredBuffer.byteLength); // 16
// Utilisation pratique - streaming de données
class StreamBuffer {
constructor(initialSize = 1024) {
this.buffer = new ArrayBuffer(initialSize, { maxByteLength: initialSize * 10 });
this.position = 0;
}
append(data) {
const needed = this.position + data.byteLength;
if (needed > this.buffer.byteLength) {
// Redimensionnement automatique
const newSize = Math.min(
needed * 2,
this.buffer.maxByteLength
);
this.buffer.resize(newSize);
}
const view = new Uint8Array(this.buffer, this.position);
view.set(new Uint8Array(data));
this.position += data.byteLength;
}
getContents() {
return this.buffer.slice(0, this.position);
}
}
6. Temporal API (Stage 3) - Gestion moderne des dates
// Attention: Temporal est encore en développement (Stage 3)
// Mais voici un aperçu de son utilisation future
// Création de dates précises
const date = Temporal.PlainDate.from('2024-03-15');
const time = Temporal.PlainTime.from('14:30:00');
const dateTime = Temporal.PlainDateTime.from('2024-03-15T14:30:00');
// Calculs de dates intuitifs
const nextWeek = date.add({ days: 7 });
const lastMonth = date.subtract({ months: 1 });
// Comparaisons faciles
const isBefore = date.compare(nextWeek) < 0; // true
// Gestion des fuseaux horaires
const zonedDateTime = Temporal.ZonedDateTime.from('2024-03-15T14:30:00[Europe/Paris]');
const inTokyo = zonedDateTime.withTimeZone('Asia/Tokyo');
// Formatage flexible
const formatted = dateTime.toLocaleString('fr-FR', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
// Durées précises
const duration = Temporal.Duration.from({ hours: 2, minutes: 30 });
const later = time.add(duration); // 17:00:00
Améliorations de Performance
7. Optimisations des Regex et String Matching
// Nouvelles fonctionnalités regex ES2024
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/g;
const text = "Today is 2024-03-15 and tomorrow is 2024-03-16";
// Utilisation des named groups de manière plus fluide
for (const match of text.matchAll(regex)) {
const { year, month, day } = match.groups;
console.log(`Date: ${day}/${month}/${year}`);
}
// Optimisation des recherches de patterns
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const emails = [
'user@example.com',
'invalid-email',
'another@test.org',
'bad@'
];
// Méthode optimisée pour validation en lot
const validEmails = emails.filter(email => emailPattern.test(email));
Intégration avec les Web APIs Modernes
8. Web Streams et ES2024
// Combinaison de Array.fromAsync avec ReadableStream
async function processStream(stream) {
const reader = stream.getReader();
async function* streamGenerator() {
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
yield value;
}
} finally {
reader.releaseLock();
}
}
// ES2024 - traitement élégant du stream
return await Array.fromAsync(
streamGenerator(),
async (chunk) => {
// Traitement asynchrone de chaque chunk
return await processChunk(chunk);
}
);
}
// Usage avec fetch
const response = await fetch('https://api.example.com/large-dataset');
const processedData = await processStream(response.body);
Bonnes Pratiques ES2024
- Array.fromAsync() : Parfait pour traiter des collections asynchrones
- Object.groupBy() : Remplace les implémentations personnelles de regroupement
- Promise.withResolvers() : Idéal pour les patterns de contrôle complexes
- String validation : Essentiel pour la sécurité des applications
- ArrayBuffer dynamique : Optimise la gestion mémoire
Conclusion
ES2024 continue d'améliorer JavaScript avec des fonctionnalités qui résolvent des problèmes concrets des développeurs. Ces ajouts rendent le code plus lisible, plus performant et plus sûr.