📋 Inhaltsverzeichnis
Webentwicklung10 min Lesezeit2025-06-12
JavaScript ES2024Features
Neueste JavaScript-Features und deren praktische Anwendung – Comprehensive Guide für moderne JavaScript-Entwicklung mit ES2024.
JavaScriptES2024Modern JSECMAScriptWeb APIs
🚀 JavaScript ES2024 Overview
ECMAScript 2024 (ES15) bringt wieder neue Features, die JavaScript-Entwicklung effizienter und ausdrucksvoller machen. Diese Version fokussiert sich auf Array-Manipulationen, RegExp-Verbesserungen und neue Promise-Methoden.
- Neue Array-Methoden: groupBy, toSorted, toReversed, toSpliced
- RegExp Verbesserungen: Named Capture Groups, Unicode Sets
- Promise.withResolvers(): Neue Promise-Konstruktor-Alternative
- Performance: Optimierte Implementierungen für bessere Performance
🔢 Neue Array-Methoden
Array.prototype.toSorted():
// Nicht-mutierende Sortierung
const numbers = [3, 1, 4, 1, 5, 9];
// Alt: Mutiert das ursprüngliche Array
const sortedOld = numbers.sort(); // numbers wird verändert!
// Neu: Gibt neues Array zurück
const sortedNew = numbers.toSorted(); // numbers bleibt unverändert
console.log(numbers); // [3, 1, 4, 1, 5, 9] (unverändert)
console.log(sortedNew); // [1, 1, 3, 4, 5, 9]
// Mit Custom Comparator
const users = [
{ name: 'Alice', age: 30 },
{ name: 'Bob', age: 25 },
{ name: 'Charlie', age: 35 }
];
const sortedByAge = users.toSorted((a, b) => a.age - b.age);
console.log(sortedByAge);
// [{ name: 'Bob', age: 25 }, { name: 'Alice', age: 30 }, { name: 'Charlie', age: 35 }]
// Funktional mit Chaining
const processedData = data
.filter(item => item.active)
.toSorted((a, b) => a.priority - b.priority)
.map(item => ({ ...item, processed: true }));
Array.prototype.toReversed():
// Nicht-mutierende Umkehrung
const items = ['a', 'b', 'c', 'd'];
// Alt: Mutiert das ursprüngliche Array
const reversedOld = items.reverse(); // items wird verändert!
// Neu: Gibt neues Array zurück
const reversedNew = items.toReversed(); // items bleibt unverändert
console.log(items); // ['a', 'b', 'c', 'd'] (unverändert)
console.log(reversedNew); // ['d', 'c', 'b', 'a']
// Praktisches Beispiel: Breadcrumb Navigation
function createBreadcrumb(path) {
return path
.split('/')
.filter(Boolean)
.toReversed()
.map((segment, index) => ({
name: segment,
isLast: index === 0
}));
}
console.log(createBreadcrumb('/wiki/javascript/es2024'));
// [
// { name: 'es2024', isLast: true },
// { name: 'javascript', isLast: false },
// { name: 'wiki', isLast: false }
// ]
Array.prototype.toSpliced():
// Nicht-mutierende Splice-Operation
const items = ['apple', 'banana', 'cherry', 'date'];
// Alt: Mutiert das ursprüngliche Array
const splicedOld = items.splice(1, 2, 'orange', 'grape'); // items wird verändert!
// Neu: Gibt neues Array zurück
const splicedNew = items.toSpliced(1, 2, 'orange', 'grape'); // items bleibt unverändert
console.log(items); // ['apple', 'banana', 'cherry', 'date'] (unverändert)
console.log(splicedNew); // ['apple', 'orange', 'grape', 'date']
// Praktisches Beispiel: Todo-Liste Update
class TodoList {
constructor() {
this.todos = [];
}
addTodo(todo, index = this.todos.length) {
this.todos = this.todos.toSpliced(index, 0, todo);
return this;
}
removeTodo(index) {
this.todos = this.todos.toSpliced(index, 1);
return this;
}
updateTodo(index, updatedTodo) {
this.todos = this.todos.toSpliced(index, 1, updatedTodo);
return this;
}
}
const todoList = new TodoList();
todoList
.addTodo({ id: 1, text: 'Learn ES2024', done: false })
.addTodo({ id: 2, text: 'Write article', done: false })
.updateTodo(0, { id: 1, text: 'Learn ES2024', done: true });
Array.prototype.with():
// Nicht-mutierende Index-basierte Ersetzung
const fruits = ['apple', 'banana', 'cherry'];
// Alt: Mutation über Index-Zuweisung
fruits[1] = 'orange'; // fruits wird verändert!
// Neu: Gibt neues Array zurück
const newFruits = fruits.with(1, 'orange'); // fruits bleibt unverändert
console.log(fruits); // ['apple', 'banana', 'cherry'] (unverändert)
console.log(newFruits); // ['apple', 'orange', 'cherry']
// Negative Indizes unterstützt
const lastChanged = fruits.with(-1, 'grape');
console.log(lastChanged); // ['apple', 'banana', 'grape']
// Praktisches Beispiel: State Update in React
function useArrayState(initialArray) {
const [array, setArray] = useState(initialArray);
const updateItem = useCallback((index, newValue) => {
setArray(prevArray => prevArray.with(index, newValue));
}, []);
const updateLastItem = useCallback((newValue) => {
setArray(prevArray => prevArray.with(-1, newValue));
}, []);
return [array, { updateItem, updateLastItem }];
}
🔍 RegExp Verbesserungen
Unicode Set Notation (v-Flag):
// Neue v-Flag für erweiterte Unicode-Unterstützung
// Ersetzt die u-Flag mit mehr Features
// Character Class Subtraktion
const asciiLettersOnly = /^[\p{ASCII}--\p{Cc}]+$/v;
console.log(asciiLettersOnly.test('Hello')); // true
console.log(asciiLettersOnly.test('Hëllo')); // false
// String Literals in Character Classes
const emojiPattern = /[\q{👨💻|👩💻|🧑💻}]/v;
console.log(emojiPattern.test('👨💻')); // true
console.log(emojiPattern.test('👩💻')); // true
// Intersection von Character Classes
const hexDigits = /[\p{ASCII_Hex_Digit}&&[0-9A-F]]/v;
console.log(hexDigits.test('A')); // true
console.log(hexDigits.test('G')); // false
// Praktisches Beispiel: Email Validation
const emailPattern = /^[\p{L}\p{N}._%-]+@[\p{L}\p{N}.-]+\.[\p{L}]{2,}$/v;
function validateEmail(email) {
return emailPattern.test(email);
}
console.log(validateEmail('user@example.com')); // true
console.log(validateEmail('üser@dömail.com')); // true (Unicode support)
// Erweiterte Unicode Property Escapes
const modernTextPattern = /^[\p{L}\p{N}\p{M}\p{Z}\p{P}&&[^\p{Cc}]]+$/v;
function isValidModernText(text) {
return modernTextPattern.test(text);
}
Named Capture Groups Verbesserungen:
// Erweiterte Named Capture Groups
const datePattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const urlPattern = /^(?<protocol>https?):\/\/(?<host>[^\/]+)(?<path>\/.*)?$/;
// Destrukturing mit Named Groups
function parseDate(dateString) {
const match = dateString.match(datePattern);
if (!match) return null;
const { year, month, day } = match.groups;
return new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
}
console.log(parseDate('2024-06-12')); // Date object
// URL Parser mit Named Groups
function parseURL(url) {
const match = url.match(urlPattern);
if (!match) return null;
return {
protocol: match.groups.protocol,
host: match.groups.host,
path: match.groups.path || '/',
port: match.groups.host.includes(':') ?
match.groups.host.split(':')[1] :
(match.groups.protocol === 'https' ? '443' : '80')
};
}
console.log(parseURL('https://example.com/path'));
// { protocol: 'https', host: 'example.com', path: '/path', port: '443' }
// Template String Replacement mit Named Groups
const template = 'Hello {{name}}, you have {{count}} messages';
const templatePattern = /\{\{(?<key>\w+)\}\}/g;
function renderTemplate(template, data) {
return template.replace(templatePattern, (match, key) => {
return data[key] || match;
});
}
console.log(renderTemplate(template, { name: 'Alice', count: 5 }));
// "Hello Alice, you have 5 messages"
🤝 Promise.withResolvers()
Promise.withResolvers() Grundlagen:
// Neue Art, Promises zu erstellen mit externen Resolver/Reject
// Ersetzt das übliche Promise-Constructor Pattern
// Alt: Promise Constructor Pattern
function createPromiseOld() {
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
}
// Neu: Promise.withResolvers()
function createPromiseNew() {
return Promise.withResolvers();
}
const { promise, resolve, reject } = Promise.withResolvers();
// Promise später auflösen
setTimeout(() => {
resolve('Success!');
}, 1000);
promise.then(console.log); // "Success!" nach 1 Sekunde
Praktische Anwendungen:
// Event-basierte Promise Resolution
class EventPromise {
constructor() {
this.promises = new Map();
}
waitFor(eventName) {
if (!this.promises.has(eventName)) {
const { promise, resolve } = Promise.withResolvers();
this.promises.set(eventName, { promise, resolve });
}
return this.promises.get(eventName).promise;
}
emit(eventName, data) {
if (this.promises.has(eventName)) {
const { resolve } = this.promises.get(eventName);
resolve(data);
this.promises.delete(eventName);
}
}
}
const eventManager = new EventPromise();
// Warten auf Event
eventManager.waitFor('user-login').then(user => {
console.log('User logged in:', user);
});
// Event später auslösen
setTimeout(() => {
eventManager.emit('user-login', { id: 1, name: 'Alice' });
}, 2000);
// Async/Await Resource Loader
class ResourceLoader {
constructor() {
this.cache = new Map();
this.loading = new Map();
}
async load(url) {
// Cache hit
if (this.cache.has(url)) {
return this.cache.get(url);
}
// Already loading
if (this.loading.has(url)) {
return this.loading.get(url).promise;
}
// Start loading
const { promise, resolve, reject } = Promise.withResolvers();
this.loading.set(url, { promise, resolve, reject });
try {
const response = await fetch(url);
const data = await response.json();
this.cache.set(url, data);
this.loading.delete(url);
resolve(data);
return data;
} catch (error) {
this.loading.delete(url);
reject(error);
throw error;
}
}
}
const loader = new ResourceLoader();
// Mehrere gleichzeitige Requests für dieselbe Resource
// werden automatisch dedupliziert
Promise.all([
loader.load('/api/users'),
loader.load('/api/users'), // Verwendet denselben Promise
loader.load('/api/users') // Verwendet denselben Promise
]).then(results => {
console.log('All loaded:', results);
});
// WebSocket Promise Wrapper
class WebSocketPromise {
constructor(url) {
this.ws = new WebSocket(url);
this.pendingRequests = new Map();
this.ws.onmessage = (event) => {
const { id, data, error } = JSON.parse(event.data);
if (this.pendingRequests.has(id)) {
const { resolve, reject } = this.pendingRequests.get(id);
this.pendingRequests.delete(id);
if (error) {
reject(new Error(error));
} else {
resolve(data);
}
}
};
}
send(data) {
const id = Math.random().toString(36).substr(2, 9);
const { promise, resolve, reject } = Promise.withResolvers();
this.pendingRequests.set(id, { resolve, reject });
this.ws.send(JSON.stringify({ id, data }));
return promise;
}
}
const wsClient = new WebSocketPromise('ws://localhost:8080');
// WebSocket Request als Promise
wsClient.send({ action: 'getUser', userId: 123 })
.then(user => console.log('User:', user))
.catch(error => console.error('Error:', error));
📅 Temporal API (Preview)
Die Temporal API ist noch in Stage 3, wird aber voraussichtlich bald finalisiert. Sie löst viele Probleme mit dem Date-Objekt.
Temporal Grundlagen:
// Temporal API (sobald verfügbar)
// Modernere, immutable Date/Time API
// PlainDate für Datumsoperationen ohne Zeit
const today = Temporal.PlainDate.from('2024-06-12');
const tomorrow = today.add({ days: 1 });
const lastWeek = today.subtract({ weeks: 1 });
console.log(today.toString()); // "2024-06-12"
console.log(tomorrow.toString()); // "2024-06-13"
console.log(lastWeek.toString()); // "2024-06-05"
// PlainTime für Zeit ohne Datum
const noon = Temporal.PlainTime.from('12:00:00');
const inOneHour = noon.add({ hours: 1 });
console.log(noon.toString()); // "12:00:00"
console.log(inOneHour.toString()); // "13:00:00"
// ZonedDateTime für vollständige Datum/Zeit mit Zeitzone
const meeting = Temporal.ZonedDateTime.from({
year: 2024,
month: 6,
day: 12,
hour: 14,
minute: 30,
timeZone: 'Europe/Berlin'
});
const meetingInNY = meeting.withTimeZone('America/New_York');
console.log(meeting.toString()); // "2024-06-12T14:30:00+02:00[Europe/Berlin]"
console.log(meetingInNY.toString()); // "2024-06-12T08:30:00-04:00[America/New_York]"
// Duration für Zeitspannen
const duration = Temporal.Duration.from({ hours: 2, minutes: 30 });
const endTime = meeting.add(duration);
console.log(endTime.toString()); // "2024-06-12T17:00:00+02:00[Europe/Berlin]"
// Vergleiche sind einfacher und zuverlässiger
const date1 = Temporal.PlainDate.from('2024-06-12');
const date2 = Temporal.PlainDate.from('2024-06-13');
console.log(Temporal.PlainDate.compare(date1, date2)); // -1 (date1 < date2)
console.log(date1.equals(date2)); // false
console.log(date1.since(date2).toString()); // "-P1D" (1 Tag weniger)
Praktische Temporal Anwendungen:
// Business Logic mit Temporal
class BusinessCalendar {
constructor(timeZone = 'Europe/Berlin') {
this.timeZone = timeZone;
this.workingHours = {
start: Temporal.PlainTime.from('09:00'),
end: Temporal.PlainTime.from('17:00')
};
this.workingDays = [1, 2, 3, 4, 5]; // Mo-Fr
}
isWorkingDay(date) {
const plainDate = Temporal.PlainDate.from(date);
return this.workingDays.includes(plainDate.dayOfWeek);
}
getNextWorkingDay(date) {
let nextDay = Temporal.PlainDate.from(date).add({ days: 1 });
while (!this.isWorkingDay(nextDay)) {
nextDay = nextDay.add({ days: 1 });
}
return nextDay;
}
scheduleDelivery(orderDate, deliveryDays = 3) {
let currentDate = Temporal.PlainDate.from(orderDate);
let workingDaysAdded = 0;
while (workingDaysAdded < deliveryDays) {
currentDate = this.getNextWorkingDay(currentDate);
workingDaysAdded++;
}
return currentDate;
}
getWorkingHoursInTimeZone(date, targetTimeZone) {
const startDateTime = date.toZonedDateTime({
timeZone: this.timeZone,
plainTime: this.workingHours.start
});
const endDateTime = date.toZonedDateTime({
timeZone: this.timeZone,
plainTime: this.workingHours.end
});
return {
start: startDateTime.withTimeZone(targetTimeZone),
end: endDateTime.withTimeZone(targetTimeZone)
};
}
}
const calendar = new BusinessCalendar('Europe/Berlin');
const orderDate = Temporal.PlainDate.from('2024-06-12');
const deliveryDate = calendar.scheduleDelivery(orderDate, 3);
console.log(`Order: ${orderDate}, Delivery: ${deliveryDate}`);
// Recurring Events
class RecurringEvent {
constructor(startDate, pattern) {
this.startDate = Temporal.PlainDate.from(startDate);
this.pattern = pattern; // { type: 'weekly', interval: 2 }
}
getOccurrences(count = 10) {
const occurrences = [this.startDate];
let currentDate = this.startDate;
for (let i = 1; i < count; i++) {
switch (this.pattern.type) {
case 'daily':
currentDate = currentDate.add({ days: this.pattern.interval || 1 });
break;
case 'weekly':
currentDate = currentDate.add({ weeks: this.pattern.interval || 1 });
break;
case 'monthly':
currentDate = currentDate.add({ months: this.pattern.interval || 1 });
break;
}
occurrences.push(currentDate);
}
return occurrences;
}
}
const weeklyMeeting = new RecurringEvent('2024-06-12', { type: 'weekly', interval: 1 });
const meetings = weeklyMeeting.getOccurrences(5);
console.log('Weekly meetings:', meetings.map(d => d.toString()));
⚡ Atomics.waitAsync()
Asynchrone Atomic Operations:
// Atomics.waitAsync() für nicht-blockierende Warteoperationen
// Nützlich für SharedArrayBuffer und Worker-Kommunikation
// Shared Memory Setup
const sharedBuffer = new SharedArrayBuffer(1024);
const sharedArray = new Int32Array(sharedBuffer);
// Worker Script (worker.js)
// In Worker:
self.onmessage = async function(e) {
const { sharedBuffer, index, expectedValue, timeout } = e.data;
const sharedArray = new Int32Array(sharedBuffer);
// Asynchron auf Wertänderung warten
const result = Atomics.waitAsync(sharedArray, index, expectedValue, timeout);
if (result.async) {
// Promise-basiertes Warten
const waitResult = await result.value;
if (waitResult === 'ok') {
self.postMessage({
type: 'value-changed',
newValue: Atomics.load(sharedArray, index)
});
} else if (waitResult === 'timed-out') {
self.postMessage({ type: 'timeout' });
}
} else {
// Sofortige Rückgabe
self.postMessage({
type: 'immediate',
result: result.value
});
}
};
// Main Thread
class SharedCounter {
constructor() {
this.sharedBuffer = new SharedArrayBuffer(16);
this.sharedArray = new Int32Array(this.sharedBuffer);
this.workers = [];
// Initialize counter
Atomics.store(this.sharedArray, 0, 0);
}
addWorker() {
const worker = new Worker('counter-worker.js');
worker.onmessage = (e) => {
const { type, newValue } = e.data;
if (type === 'value-changed') {
console.log(`Counter changed to: ${newValue}`);
this.notifyListeners(newValue);
}
};
this.workers.push(worker);
return worker;
}
waitForValue(expectedValue, timeout = 5000) {
return new Promise((resolve, reject) => {
const worker = this.addWorker();
worker.postMessage({
sharedBuffer: this.sharedBuffer,
index: 0,
expectedValue,
timeout
});
worker.onmessage = (e) => {
const { type, newValue, result } = e.data;
if (type === 'value-changed') {
resolve(newValue);
} else if (type === 'timeout') {
reject(new Error('Timeout waiting for value'));
} else if (type === 'immediate') {
resolve(result);
}
worker.terminate();
};
});
}
increment() {
const oldValue = Atomics.load(this.sharedArray, 0);
const newValue = Atomics.add(this.sharedArray, 0, 1) + 1;
// Notify all waiting workers
Atomics.notify(this.sharedArray, 0);
return newValue;
}
getValue() {
return Atomics.load(this.sharedArray, 0);
}
}
// Usage
const counter = new SharedCounter();
// Wait for counter to reach 5
counter.waitForValue(5).then(value => {
console.log(`Counter reached ${value}!`);
});
// Increment counter in intervals
setInterval(() => {
const newValue = counter.increment();
console.log(`Counter incremented to: ${newValue}`);
}, 1000);
🔤 Well-formed Unicode Strings
String.prototype.isWellFormed() und toWellFormed():
// Neue Methoden für Unicode-Validierung und -Korrektur
// isWellFormed() - Prüft auf korrekte Unicode-Sequenzen
const validString = 'Hello 👋 World';
const invalidString = 'Hello \uD800 World'; // Lone surrogate
console.log(validString.isWellFormed()); // true
console.log(invalidString.isWellFormed()); // false
// toWellFormed() - Korrigiert ungültige Unicode-Sequenzen
const correctedString = invalidString.toWellFormed();
console.log(correctedString); // "Hello � World" (replacement character)
// Praktische Anwendung: Sichere String-Verarbeitung
class UnicodeValidator {
static validateInput(input) {
if (typeof input !== 'string') {
throw new TypeError('Input must be a string');
}
if (!input.isWellFormed()) {
console.warn('Invalid Unicode sequence detected, auto-correcting...');
return input.toWellFormed();
}
return input;
}
static safeStringify(obj) {
const jsonString = JSON.stringify(obj);
if (!jsonString.isWellFormed()) {
return jsonString.toWellFormed();
}
return jsonString;
}
static safeDatabaseInsert(data) {
// Ensure all string values are well-formed before database insertion
const cleanData = {};
for (const [key, value] of Object.entries(data)) {
if (typeof value === 'string') {
cleanData[key] = UnicodeValidator.validateInput(value);
} else {
cleanData[key] = value;
}
}
return cleanData;
}
}
// Usage examples
const userInput = 'User comment with invalid \uD800 surrogate';
const safeInput = UnicodeValidator.validateInput(userInput);
const userData = {
name: 'John Doe',
comment: 'Invalid unicode \uD800\uD800',
email: 'john@example.com'
};
const safeUserData = UnicodeValidator.safeDatabaseInsert(userData);
console.log(safeUserData);
// { name: 'John Doe', comment: 'Invalid unicode ��', email: 'john@example.com' }
// Web API Integration
class SafeWebAPI {
static async post(url, data) {
// Ensure all string data is well-formed before sending
const safeData = {};
for (const [key, value] of Object.entries(data)) {
if (typeof value === 'string') {
safeData[key] = value.isWellFormed() ? value : value.toWellFormed();
} else {
safeData[key] = value;
}
}
return fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(safeData)
});
}
static async processResponse(response) {
const text = await response.text();
if (!text.isWellFormed()) {
console.warn('Response contains invalid Unicode, correcting...');
return text.toWellFormed();
}
return text;
}
}
📦 Resizable ArrayBuffers
Resizable ArrayBuffer und SharedArrayBuffer:
// Resizable ArrayBuffer - kann zur Laufzeit vergrößert werden
const buffer = new ArrayBuffer(1024, { maxByteLength: 4096 });
const view = new Uint8Array(buffer);
console.log(buffer.byteLength); // 1024
console.log(buffer.maxByteLength); // 4096
console.log(buffer.resizable); // true
// Buffer vergrößern
buffer.resize(2048);
console.log(buffer.byteLength); // 2048
// Neue View auf vergrößerten Buffer
const newView = new Uint8Array(buffer);
console.log(newView.length); // 2048
// Praktische Anwendung: Dynamic Data Buffer
class DynamicBuffer {
constructor(initialSize = 1024, maxSize = 1024 * 1024) {
this.buffer = new ArrayBuffer(initialSize, { maxByteLength: maxSize });
this.view = new Uint8Array(this.buffer);
this.position = 0;
}
ensureCapacity(additionalBytes) {
const requiredSize = this.position + additionalBytes;
if (requiredSize > this.buffer.byteLength) {
const newSize = Math.min(
Math.max(this.buffer.byteLength * 2, requiredSize),
this.buffer.maxByteLength
);
if (newSize > this.buffer.maxByteLength) {
throw new Error('Buffer would exceed maximum size');
}
this.buffer.resize(newSize);
this.view = new Uint8Array(this.buffer);
}
}
writeBytes(data) {
this.ensureCapacity(data.length);
this.view.set(data, this.position);
this.position += data.length;
}
writeString(str) {
const encoder = new TextEncoder();
const encoded = encoder.encode(str);
this.writeBytes(encoded);
}
writeUint32(value) {
this.ensureCapacity(4);
const dataView = new DataView(this.buffer);
dataView.setUint32(this.position, value, true); // little endian
this.position += 4;
}
toArrayBuffer() {
return this.buffer.slice(0, this.position);
}
reset() {
this.position = 0;
}
get size() {
return this.position;
}
get capacity() {
return this.buffer.byteLength;
}
}
// Usage
const dynBuffer = new DynamicBuffer(64, 1024);
dynBuffer.writeString('Hello ');
dynBuffer.writeString('World!');
dynBuffer.writeUint32(42);
console.log(`Used: ${dynBuffer.size}, Capacity: ${dynBuffer.capacity}`);
// SharedArrayBuffer Resizable (für Worker)
class SharedDynamicBuffer {
constructor(initialSize = 1024, maxSize = 1024 * 1024) {
this.sharedBuffer = new SharedArrayBuffer(initialSize, { maxByteLength: maxSize });
this.header = new Int32Array(this.sharedBuffer, 0, 2); // [position, capacity]
this.data = new Uint8Array(this.sharedBuffer, 8); // Daten nach Header
// Initialize header
Atomics.store(this.header, 0, 0); // position
Atomics.store(this.header, 1, initialSize - 8); // capacity
}
ensureCapacity(additionalBytes) {
const currentPosition = Atomics.load(this.header, 0);
const currentCapacity = Atomics.load(this.header, 1);
const requiredSize = currentPosition + additionalBytes;
if (requiredSize > currentCapacity) {
const newSize = Math.min(
Math.max(this.sharedBuffer.byteLength * 2, requiredSize + 8),
this.sharedBuffer.maxByteLength
);
this.sharedBuffer.resize(newSize);
this.data = new Uint8Array(this.sharedBuffer, 8);
Atomics.store(this.header, 1, newSize - 8);
}
}
atomicWrite(data) {
this.ensureCapacity(data.length);
const position = Atomics.load(this.header, 0);
// Write data
this.data.set(data, position);
// Update position atomically
Atomics.store(this.header, 0, position + data.length);
return position;
}
getSharedBuffer() {
return this.sharedBuffer;
}
}
// Usage with Workers
const sharedDynBuffer = new SharedDynamicBuffer(128, 2048);
// In Worker
const worker = new Worker(`
const sharedBuffer = self.sharedBuffer;
const header = new Int32Array(sharedBuffer, 0, 2);
const data = new Uint8Array(sharedBuffer, 8);
self.postMessage({
position: Atomics.load(header, 0),
capacity: Atomics.load(header, 1)
});
`);
💡 Praktische Anwendungen
Kombinierte ES2024 Features:
// Practical Example: Advanced Data Processing Pipeline
class DataProcessor {
constructor() {
this.cache = new Map();
this.processors = [];
}
// Using Array.prototype.toSorted() and with()
processArray(data, sortKey) {
return data
.toSorted((a, b) => a[sortKey] - b[sortKey]) // Non-mutating sort
.with(0, { ...data[0], processed: true }); // Mark first item
}
// Using Promise.withResolvers() for async processing
createProcessor() {
const { promise, resolve, reject } = Promise.withResolvers();
const processor = {
id: Math.random().toString(36).substr(2, 9),
promise,
resolve,
reject,
start: Date.now()
};
this.processors.push(processor);
return processor;
}
// Using RegExp v-flag for validation
validateEmailList(emails) {
const emailPattern = /^[\p{L}\p{N}._%-]+@[\p{L}\p{N}.-]+\.[\p{L}]{2,}$/v;
return emails
.filter(email => email.isWellFormed()) // Well-formed Unicode
.filter(email => emailPattern.test(email))
.toSorted(); // Sort alphabetically
}
// Comprehensive example
async processUserData(users) {
const processor = this.createProcessor();
try {
// Validate and clean user data
const validUsers = users
.filter(user => user.email.isWellFormed())
.map(user => ({
...user,
email: user.email.toWellFormed()
}));
// Sort by registration date (non-mutating)
const sortedUsers = validUsers.toSorted((a, b) =>
new Date(a.registeredAt) - new Date(b.registeredAt)
);
// Mark VIP users (non-mutating update)
const processedUsers = sortedUsers.map((user, index) => {
if (user.purchases > 1000) {
return sortedUsers.with(index, { ...user, vip: true });
}
return user;
});
processor.resolve(processedUsers);
return processor.promise;
} catch (error) {
processor.reject(error);
throw error;
}
}
}
// Modern Email Validator using ES2024 features
class EmailValidator {
constructor() {
// Using Unicode Sets (v-flag)
this.patterns = {
basic: /^[\p{L}\p{N}._%-]+@[\p{L}\p{N}.-]+\.[\p{L}]{2,}$/v,
strict: /^[\p{ASCII}&&[^\p{Cc}\p{Z}]]+@[\p{ASCII}&&[^\p{Cc}\p{Z}]]+\.[\p{L}]{2,}$/v
};
}
validateBatch(emails) {
const results = emails.map(email => ({
email: email.isWellFormed() ? email : email.toWellFormed(),
valid: false,
errors: []
}));
return results
.map((result, index) => {
const email = result.email;
if (!email.isWellFormed()) {
return results.with(index, {
...result,
errors: [...result.errors, 'Invalid Unicode sequence']
});
}
if (!this.patterns.basic.test(email)) {
return results.with(index, {
...result,
errors: [...result.errors, 'Invalid email format']
});
}
return results.with(index, { ...result, valid: true });
});
}
getValidEmails(emails) {
return this.validateBatch(emails)
.filter(result => result.valid)
.map(result => result.email)
.toSorted(); // Sort alphabetically
}
}
// Usage
const processor = new DataProcessor();
const validator = new EmailValidator();
const userData = [
{ name: 'Alice', email: 'alice@example.com', purchases: 1500, registeredAt: '2024-01-15' },
{ name: 'Bob', email: 'bob@test.com', purchases: 500, registeredAt: '2024-02-20' },
{ name: 'Charlie', email: 'charlie@demo.org', purchases: 2000, registeredAt: '2024-01-10' }
];
processor.processUserData(userData).then(users => {
console.log('Processed users:', users);
});
const emails = ['alice@example.com', 'invalid-email', 'bob@test.com\uD800'];
const validEmails = validator.getValidEmails(emails);
console.log('Valid emails:', validEmails);
Performance-Tipps:
- Verwende
toSorted()
,toReversed()
,with()
für immutable Operations - RegExp v-Flag bietet bessere Unicode-Performance
Promise.withResolvers()
vermeidet nested Promise-KonstruktorenisWellFormed()
prüfen vor String-Operationen- Resizable Buffers für dynamische Datenstrukturen nutzen
📚 Verwandte Artikel
TypeScript für React Entwickler
Erfahren Sie mehr über verwandte Webentwicklungs-Themen.
18 min LesezeitReact Best Practices 2025
Erfahren Sie mehr über verwandte Webentwicklungs-Themen.
15 min LesezeitNext.js Performance Optimization
Erfahren Sie mehr über verwandte Webentwicklungs-Themen.
12 min Lesezeit