Tavo-IT Logo
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-Konstruktoren
  • isWellFormed() prüfen vor String-Operationen
  • Resizable Buffers für dynamische Datenstrukturen nutzen