Start · Sprachen · JavaScript · Referenz · FinalizationRegistry.prototype.register()

FinalizationRegistry.prototype.register()

Methode

Registriert ein Objekt bei einer <code>FinalizationRegistry</code>. Wird das Objekt vom Garbage Collector entfernt, kann danach der Aufräum-Callback mit <code>heldValue</code> aufgerufen werden.

seit JavaScript ES2021 (Baseline: alle modernen Browser, Node.js 1 Kategorie: method-property

Signatur

FinalizationRegistry.prototype.register(target, heldValue, unregisterToken)

Beschreibung

Mit register() meldest du ein Zielobjekt (target) bei einer FinalizationRegistry an. Sobald die Speicherbereinigung (Garbage Collection) das Objekt entfernt hat, kann die JavaScript-Engine später den Callback aufrufen, den du dem Konstruktor der Registry übergeben hast. Der Callback bekommt dabei den heldValue als Argument, nicht das Zielobjekt selbst, denn das existiert zu diesem Zeitpunkt nicht mehr.

Die Registry hält das Zielobjekt nur schwach und verhindert seine Bereinigung also nicht. Den heldValue hält sie dagegen stark. Deshalb sollte er ein kleiner, eigenständiger Wert sein, etwa eine ID, ein Dateideskriptor oder ein Schlüssel in einem Cache. Verweist der heldValue auf das Zielobjekt, wird dieses nie bereinigt.

Mit dem optionalen unregisterToken kannst du die Registrierung später über unregister() wieder aufheben, z. B. wenn eine Ressource schon von Hand freigegeben wurde. Häufig nimmt man dafür das Zielobjekt selbst. Die Registry hält auch den Token nur schwach.

Typische Einsatzfälle sind das Aufräumen externer Ressourcen (WebAssembly-Speicher, Handles in nativen Bibliotheken) und das Entfernen verwaister Einträge aus einem Cache, der mit WeakRef arbeitet. Als Ersatz für eine gezielte Freigabe wie close() oder dispose() eignet sich register() nicht.

Parameter

Name Typ Default Beschreibung
$target Pflicht object | symbol Das Objekt, dessen Bereinigung beobachtet wird. Ab ES2023 sind auch nicht registrierte Symbole erlaubt (also keine Symbole aus Symbol.for()). Bei Primitivwerten wird ein TypeError ausgelöst.
$heldValue Pflicht any Der Wert, den der Aufräum-Callback erhält. Er darf nicht dasselbe Objekt wie target sein, sonst wird ein TypeError ausgelöst. Die Registry hält ihn stark. Er sollte daher nicht auf target verweisen.
$unregisterToken object | symbol | undefined undefined Optionaler Token, mit dem du die Registrierung über unregister(token) wieder aufheben kannst. Die Registry hält ihn nur schwach. Ist er weder Objekt noch nicht registriertes Symbol noch undefined, wird ein TypeError ausgelöst.

Rückgabewert

Typ
undefined
Beschreibung
Kein Rückgabewert. Die Registrierung wird intern gespeichert.

Beispiele

Grundlegende Registrierung

const registry = new FinalizationRegistry((heldValue) => {
  console.log(`Aufgeräumt: ${heldValue}`);
});

let user = { name: 'Anna' };
registry.register(user, 'user-42');

// Letzte starke Referenz entfernen – ab jetzt darf der GC das Objekt einsammeln
user = null;
// Irgendwann (oder nie) erscheint: "Aufgeräumt: user-42"
Aufgeräumt: user-42 (Zeitpunkt nicht garantiert)

Externe Ressource mit Abmelde-Token

const handles = new FinalizationRegistry((handleId) => {
  console.warn(`Handle ${handleId} wurde nicht geschlossen – räume auf.`);
  // nativeClose(handleId);
});

class FileHandle {
  static #nextId = 1;
  #id = FileHandle.#nextId++;

  constructor() {
    // this dient gleichzeitig als Ziel und als Abmelde-Token
    handles.register(this, this.#id, this);
  }

  close() {
    // Von Hand geschlossen: Aufräum-Callback wird nicht mehr gebraucht
    handles.unregister(this);
    console.log(`Handle ${this.#id} geschlossen.`);
  }
}

const f = new FileHandle();
f.close();
Handle 1 geschlossen.

Cache mit WeakRef und automatischem Aufräumen

const cache = new Map();
const cleanup = new FinalizationRegistry((key) => {
  const ref = cache.get(key);
  // Nur löschen, wenn der Eintrag nicht inzwischen ersetzt wurde
  if (ref && !ref.deref()) cache.delete(key);
});

const setCached = (key, value) => {
  cache.set(key, new WeakRef(value));
  cleanup.register(value, key);
};

const getCached = (key) => cache.get(key)?.deref();

setCached('bild-1', { pixels: new Uint8Array(1024) });
console.log(getCached('bild-1') !== undefined);
true

Fehlerfälle

const registry = new FinalizationRegistry(() => {});
const obj = {};

try { registry.register(42, 'x'); } catch (e) { console.log(e.name); }
try { registry.register(obj, obj); } catch (e) { console.log(e.name); }
try { registry.register(obj, 'x', 'token'); } catch (e) { console.log(e.name); }
TypeError TypeError TypeError

// Wichtig · Fallstricke

  • Kein garantierter Aufruf: Ob und wann der Callback läuft, entscheidet die Engine. Er kann stark verzögert kommen, beim Beenden des Programms oder beim Schließen des Tabs ganz ausfallen. Verlasse dich für wichtige Programmlogik nicht darauf.
  • Kein Verweis auf das Ziel im heldValue: Zeigt der heldValue direkt oder indirekt auf target, bleibt das Ziel für immer erreichbar. Das führt zu einem Speicherleck.
  • Closures beachten: Auch der Callback darf das Zielobjekt nicht über eine Closure festhalten.
  • Ein Objekt kann mehrfach registriert werden, auch bei derselben Registry. Jede Registrierung kann dann einen eigenen Callback-Aufruf auslösen.
  • Symbole als target oder unregisterToken sind erst ab ES2023 erlaubt (Chrome 108+, Firefox 119+, Safari 16.4+). Registrierte Symbole aus Symbol.for() führen zu einem TypeError.
  • Wann der Speicher bereinigt wird, hängt stark von der Engine ab. In DevTools oder Node.js (--expose-gc, global.gc()) kannst du die Bereinigung zum Testen von Hand auslösen. Im Browser ist das Verhalten nicht vorhersagbar.