Memory Management
The ES2021 memory-management objects WeakRef and FinalizationRegistry are not available in SSJS. The SFMC Jint engine predates them, so both are undefined.
The ES2021 memory-management objects are not available in SSJS. The SFMC server-side JavaScript engine (Jint) predates ES2021, so WeakRef and FinalizationRegistry are entirely absent. Each is undefined.
These objects only matter for long-lived programs that hold references to large objects and want to cooperate with the garbage collector. SSJS runs a short, synchronous request-scoped script and then tears the context down, so there is rarely a reason to need them — hold normal (strong) references and let the script end reclaim everything.
Status legend
| Icon | Meaning |
|---|---|
| ❌ Missing | Not available (typeof is "undefined") |
Members
| Member | ES | Status | Notes |
|---|---|---|---|
WeakRef |
ES2021 | ❌ Missing | No weak references; use normal variables |
FinalizationRegistry |
ES2021 | ❌ Missing | No GC finalization callbacks |
WeakRef
(ES2021) — ❌ Missing. WeakRef is not defined — typeof WeakRef === "undefined". There is no weak-reference mechanism. Hold a normal variable reference; it is released automatically when the request-scoped script finishes.
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: WeakRef
*
* Proves:
* 1. DEVIATION from ECMAScript 2021 marked "DEV": WeakRef does not exist in
* the SFMC Jint engine — typeof WeakRef is "undefined" (spec: "function").
* Merely READING the name does not throw; only calling it does.
* 2. Constructing it throws (spec: returns a WeakRef wrapper).
* 3. No weak-reference API leaks in by another route: WeakRef.prototype and
* a .deref() method are equally absent.
* 4. The documented replacement — a normal (strong) variable reference —
* keeps the target readable for the whole script, which is exactly the
* behaviour the page recommends relying on.
*
* NOT ASSERTED (not deterministically observable from script):
* - "it is released automatically when the request-scoped script finishes".
* Reclamation happens after the script has ended, so no statement inside
* the script can observe it. There is no gc() hook, no heap-size reading
* and no finalization callback in this engine (see the FinalizationRegistry
* chapter), so GC timing is unobservable by construction. The absence of
* any such hook IS asserted below; the timing itself is not.
*
* EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
* longer matches the documented claim and the page must be revised.
*/
function assert(id, fn, expected) {
var got;
try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === expected ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
/* 1. DEVIATION — WeakRef is absent. Reading the name is safe; it is undefined. */
assert("DEV typeof WeakRef is 'undefined' (spec: 'function')", function () { return String(typeof WeakRef); }, "undefined");
/* 2. Constructing it throws. */
assertThrows("DEV new WeakRef({}) throws (spec: returns a WeakRef)", function () { var target = {}; return new WeakRef(target); });
assert("new WeakRef({}) message is 'Unknown type: WeakRef'", function () { try { var t = {}; return new WeakRef(t); } catch (ex) { return ex.message; } }, "Unknown type: WeakRef");
assertThrows("DEV WeakRef({}) called without new throws too", function () { var target = {}; return WeakRef(target); });
/* 3. No part of the WeakRef API exists by another route. Reading a member of
* the missing global does not throw — it simply yields undefined. */
assert("WeakRef.prototype is undefined", function () { return String(typeof WeakRef.prototype); }, "undefined");
assert("a plain object has no .deref()", function () { var o = {}; return String(typeof o.deref); }, "undefined");
/* 4. The documented replacement — a normal strong reference stays readable. */
assert("strong reference keeps the target readable", function () { var target = { id: "kept" }; var ref = target; return ref.id; }, "kept");
assert("strong reference is identity-equal to the target", function () { var target = { id: "kept" }; var ref = target; return ref === target; }, true);
assert("strong reference sees later mutations of the target", function () { var target = { id: "kept" }; var ref = target; target.id = "changed"; return ref.id; }, "changed");
assert("explicit release makes the local handle undefined", function () { var target = { id: "kept" }; var ref = target; ref = undefined; return String(typeof ref); }, "undefined");
</script>
FinalizationRegistry
(ES2021) — ❌ Missing. FinalizationRegistry is not defined — typeof FinalizationRegistry === "undefined". You cannot register a callback to run when an object is garbage-collected. Perform any needed cleanup explicitly at the end of your script instead.
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: FinalizationRegistry
*
* Proves:
* 1. DEVIATION from ECMAScript 2021 marked "DEV": FinalizationRegistry does
* not exist in the SFMC Jint engine — typeof FinalizationRegistry is
* "undefined" (spec: "function"). Reading the name is safe; calling it
* is what throws.
* 2. Constructing it throws (spec: returns a registry), so no callback can
* be registered to run when an object is garbage-collected.
* 3. No registry API leaks in by another route: .prototype is unreachable
* and a plain object carries no register/unregister methods. There is
* also no gc() hook to force collection.
* 4. The documented replacement — performing cleanup explicitly at the end
* of the script — works: an ordinary function call runs deterministically
* and can release the references it owns.
*
* NOT ASSERTED (not deterministically observable from script):
* - "you cannot register a callback to run when an object is
* garbage-collected". The negative half IS asserted (no registry exists,
* construction throws), but the underlying GC event itself is unobservable:
* the engine exposes no gc() trigger, no heap statistics and no
* finalization hook, so no script can detect when — or whether — a given
* object is collected. Anything claiming to assert GC timing would be
* fabricated.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, fn, expected) {
var got;
try { got = fn(); } catch (ex) { got = "THREW: " + ex.message; }
Platform.Response.Write((got === expected ? "PASS " : "FAIL ") + id + " -> [" + got + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
/* 1. DEVIATION — FinalizationRegistry is absent. */
assert("DEV typeof FinalizationRegistry is 'undefined' (spec: 'function')", function () { return String(typeof FinalizationRegistry); }, "undefined");
/* 2. Constructing it throws, so no finalization callback can be registered. */
assertThrows("DEV new FinalizationRegistry(fn) throws (spec: returns a registry)", function () { return new FinalizationRegistry(function () { return 1; }); });
assert("new FinalizationRegistry() message is 'Unknown type: FinalizationRegistry'", function () { try { return new FinalizationRegistry(function () { return 1; }); } catch (ex) { return ex.message; } }, "Unknown type: FinalizationRegistry");
assertThrows("DEV FinalizationRegistry(fn) without new throws too", function () { return FinalizationRegistry(function () { return 1; }); });
/* 3. No part of the registry API — nor any GC hook — exists by another route.
* Reading a member of the missing global yields undefined rather than throwing. */
assert("FinalizationRegistry.prototype is undefined", function () { return String(typeof FinalizationRegistry.prototype); }, "undefined");
assert("a plain object has no .register()", function () { var o = {}; return String(typeof o.register); }, "undefined");
assert("a plain object has no .unregister()", function () { var o = {}; return String(typeof o.unregister); }, "undefined");
assert("there is no gc() hook to force collection", function () { return String(typeof gc); }, "undefined");
assert("WeakRef is absent as well, so no weak-reference API at all", function () { return String(typeof WeakRef); }, "undefined");
/* 4. The documented replacement — explicit cleanup at the end of the script. */
assert("explicit cleanup function runs deterministically", function () { var state = { open: true }; function cleanup(s) { s.open = false; return "cleaned"; } var r = cleanup(state); return r + ":" + state.open; }, "cleaned:false");
assert("explicit cleanup can drop the reference it owns", function () { var holder = { data: { big: "payload" } }; holder.data = null; return String(holder.data); }, "null");
</script>