---
title: "WeakRefs in JavaScript: Explained In Simple Terms"
description: "A WeakRef points to an object without stopping the garbage collector from freeing it. When that helps, how deref() works, and cleanup with FinalizationRegistry."
canonical: "https://www.trevorlasn.com/blog/weakref-javascipt"
author: "Trevor I. Lasn"
published: "2025-01-07"
last-updated: "2025-01-07"
topics: ["JavaScript"]
---

# WeakRefs in JavaScript: Explained In Simple Terms

A WeakRef points to an object without stopping the garbage collector from freeing it. When that helps, how deref() works, and cleanup with FinalizationRegistry.

By Trevor I. Lasn · Published 2025-01-07 · https://www.trevorlasn.com/blog/weakref-javascipt

[Browser support for `weak-references` on webstatus.dev](https://webstatus.dev/features/weak-references)

Every time you create an object in JavaScript, it takes up memory in your browser. When you're done with that object, JavaScript is supposed to clear that memory automatically. But sometimes, this cleanup doesn't happen when you expect it to.

### Strong References

A strong reference is like putting a "do not delete" tag on data in memory. When you create a normal variable in JavaScript that points to an object or value, you're creating a strong reference. 

As long as this reference exists, JavaScript's garbage collector won't remove that data from memory, even if it's not being used anywhere else.

```javascript title="JavaScript"
// Creates a strong reference to an object
const user = { 
  url: "www.trevorlasn.com" ,
  name: "Trevor Lasn"
};
```

Here, the `user` variable strongly holds onto the object, keeping it safe from garbage collection.

### Weak References

A weak reference, on the other hand, is like putting a "delete if not needed" tag on data. It points to an object or value but doesn't prevent the garbage collector from removing that data if no strong references exist. Think of it as a way to reference something without forcing it to stay in memory.

To put it simply: if an object only has weak references pointing to it, the garbage collector can clean it up. This is particularly useful when you want to reference something temporarily without causing memory leaks.

<div
  class="flex items-center text-sm pl-4 pr-2 mb-5 mt-5 text-indigo-700 rounded-lg bg-surface dark:text-indigo-300"
  role="note"
>
  <svg 
    class="flex-shrink-0 inline w-4 h-4 me-3" 
    aria-hidden="true"
    xmlns="http://www.w3.org/2000/svg"
    fill="none"
    viewBox="0 0 24 24"
    stroke="currentColor"
    stroke-width="2"
  >
    <path d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
  </svg>
  <div>
    <a href="/blog/common-causes-of-memory-leaks-in-javascript"
        class="font-medium text-md mb-1 text-indigo-700 dark:text-indigo-300 "
      >
        Common Causes of Memory Leaks in JavaScript
    </a>
  </div>
</div>

```javascript title="JavaScript"
// Create a weak reference to an object
const weakUser = new WeakRef({ 
    url: "www.trevorlasn.com",
    name: "Trevor Lasn"
});

// Access the object through the weak reference
console.log(weakUser.deref()?.name); // "Trevor Lasn"
```

The key difference is that strong references actively prevent garbage collection, while weak references allow it to happen when no strong references remain.

### Why Memory Cleanup Matters

Let's say you're building a chat app. Every time a new message appears, you format it and save some information about how it should look:

```javascript title="JavaScript"
const messageCache = new Map();

function handleNewMessage(message) {
    // Save formatted message data
    messageCache.set(message.id, {
        text: message.text,
        formattedTime: formatMessageTime(message.timestamp)
    });
}
```

<div
  class="flex items-center text-sm pl-4 pr-2 mb-5 mt-5 text-indigo-700 rounded-lg bg-surface dark:text-indigo-300"
  role="note"
>
  <svg 
    class="flex-shrink-0 inline w-4 h-4 me-3" 
    aria-hidden="true"
    xmlns="http://www.w3.org/2000/svg"
    fill="none"
    viewBox="0 0 24 24"
    stroke="currentColor"
    stroke-width="2"
  >
    <path d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
  </svg>
  <div>
    <a href="/blog/sets-and-maps-in-javascript"
        class="font-medium text-md mb-1 text-indigo-700 dark:text-indigo-300 "
      >
        If you're unfamiliar with JavaScript's `Map` methods, this deep dive is for you.
    </a>
  </div>
</div>

As users send more messages, your app uses more memory. Even when old messages scroll away and you can't see them anymore, the data stays in memory because `messageCache` still has a reference to it. Your browser can't clean it up because you're telling it "I might need this later" by keeping it in the cache.

### How Memory Cleanup Works

By default, JavaScript keeps objects in memory as long as your code can still reach them. Think of it like a game of connect-the-dots. If JavaScript can draw a line from your code to an object through references, that object stays in memory.

```javascript title="JavaScript"
let message = { text: "Hello" };  // Object created in memory
let copy = message;               // Another way to reach the same object
message = null;                   // Object still exists because 'copy' references it
copy = null;                      // Now the object can be cleared from memory
```

<br />

### What WeakRef Does

`WeakRef` creates a special kind of reference that doesn't force objects to stay in memory. It's like taking a note about where something is without actually holding onto it.

```javascript title="JavaScript"
let message = { text: "Hello" };
let weakNote = new WeakRef(message);  // Creates a weak reference

// Later...
let maybeMessage = weakNote.deref();  // Try to get the message
if (maybeMessage) {
    console.log(maybeMessage.text);  // It might work
} else {
    console.log("Message is gone");   // Or the message might be gone
}
```

When you use `WeakRef`, you're telling JavaScript "I'd like to use this object if it's still around, but it's okay to clean it up if nothing else needs it."

`WeakRef` can help when you want to track something without forcing it to stay in memory. Here's a real example where that's useful - tracking images as they load on a page:

```javascript title="JavaScript"
function trackImageLoad(img) {
    const ref = new WeakRef(img);
    const startTime = Date.now();

    const tracker = setInterval(() => {
        const image = ref.deref();
        
        // If image is gone (user navigated away or it was removed), stop tracking
        if (!image) {
            clearInterval(tracker);
            return;
        }

        // If image loaded, log how long it took
        if (image.complete) {
            const loadTime = Date.now() - startTime;
            console.log(`Image loaded in ${loadTime}ms`);
            clearInterval(tracker);
        }
    }, 100);
}

// Use it on any image
const img = document.createElement('img');
img.src = 'photo.jpg';
trackImageLoad(img);
document.body.appendChild(img);
```

This code helps us understand how long images take to load, but won't prevent the browser from cleaning up the image if the user leaves the page or if the image gets removed. 

Without `WeakRef`, we'd need to manually remove our tracker every time an image was removed, or the image would stay in memory even when it's gone from the page.

### Cleaning Up After Objects with `FinalizationRegistry`

Sometimes you need to know when an object gets cleaned up from memory. That's where `FinalizationRegistry` helps. It lets you run code when an object is removed from memory.

```javascript title="JavaScript"
// Create a registry with a cleanup function
const registry = new FinalizationRegistry(data => {
    console.log('Cleanup:', data);
});

function trackImage(img) {
    const ref = new WeakRef(img);
    
    // Register the image with some data to help us identify it
    registry.register(img, {
        src: img.src,
        trackedAt: Date.now()
    });

    return ref;
}

// Example usage
const img = document.createElement('img');
img.src = 'photo.jpg';
const imageRef = trackImage(img);

// Later, when the image is removed and garbage collected,
// our registry function will run with the data we provided
```

But be careful - you can't rely on exactly when the cleanup function will run. The browser decides when to clean up memory, and your cleanup function might run much later than you expect. This makes `FinalizationRegistry` useful for:

- Cleaning up files or resources on a server
- Logging when objects are removed
- Updating counters or statistics

Here's a more practical example combining `WeakRef` and `FinalizationRegistry` to track memory usage.

```javascript title="JavaScript"
const cleanup = new FinalizationRegistry(data => {
    console.log(`Resource cleaned up: ${data.type}`);
    updateMemoryStats(data.size, 'freed');
});

function trackResource(resource, type, size) {
    const ref = new WeakRef(resource);
    
    // Register for cleanup with useful data
    cleanup.register(resource, {
        type,
        size,
        createdAt: Date.now()
    });

    updateMemoryStats(size, 'allocated');
    return ref;
}

function updateMemoryStats(bytes, action) {
    if (action === 'allocated') {
        memoryUsed += bytes;
    } else {
        memoryUsed -= bytes;
    }
    updateMemoryDisplay();
}
```

This code helps track memory usage over time, updating statistics when objects are created and cleaned up, without preventing the cleanup from happening.

Here are some specific points included by the authors in the [proposal](https://github.com/tc39/proposal-weakrefs?tab=readme-ov-file#a-note-of-caution) that introduced `WeakRef`

<blockquote>
Garbage collectors are complicated. If an application or library depends on GC cleaning up a `WeakRef` or calling a finalizer [cleanup callback] in a timely, predictable manner, it's likely to be disappointed: the cleanup may happen much later than expected, or not at all. Sources of variability include:

- One object might be garbage-collected much sooner than another object, even if they become unreachable at the same time, e.g., due to generational collection.
- Garbage collection work can be split up over time using incremental and concurrent techniques.
- Various runtime heuristics can be used to balance memory usage, responsiveness.
- The JavaScript engine may hold references to things which look like they are unreachable (e.g., in closures, or inline caches).
- Different JavaScript engines may do these things differently, or the same engine may change its algorithms across versions.
- Complex factors may lead to objects being held alive for unexpected amounts of time, such as use with certain APIs.
</blockquote>

If you're unsure whether `WeakRef` is right for your use case, consider avoiding it. It's a powerful tool, but it's easy to misuse. If you're not sure, you probably don't need it.
