You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用WeakMap/WeakSet实现可回收的JavaScript享元对象?

Fixing Flyweight Garbage Collection with FinalizationRegistry

Great question! The core problem with your current implementation is that Map holds strong references to all your FlyweightNumber instances—even when no other code references them, they’ll never be garbage collected.

You’re right that WeakMap/WeakSet enable automatic garbage collection, but there’s a catch: WeakMap only accepts objects as keys, and your flyweight uses primitive numbers as identifiers. We can’t directly use a WeakMap to map numbers to instances. Instead, we’ll combine a regular Map for fast lookups with a FinalizationRegistry (an ES2021 feature) to clean up the Map when instances are garbage collected.

Here’s the revised implementation:

const FlyweightNumber = (function(){
    "use strict";
    const instances = new Map();
    // Create a registry to clean up the Map when instances are GC'd
    const cleanupRegistry = new FinalizationRegistry((number) => {
        instances.delete(number);
    });

    class FlyweightNumber{
        constructor(number){
            // Check if we already have an instance for this number
            const existingInstance = instances.get(number);
            if (existingInstance) {
                return existingInstance;
            }

            // If not, create a new instance
            Object.defineProperty(this, 'number', { value: number });
            instances.set(number, this);
            // Register the instance with the cleanup registry
            cleanupRegistry.register(this, number);
        }

        toString() { return this.number.toString(); }
        valueOf(){ return this.number; }
        toJSON(){ return this.number; }
    }

    return FlyweightNumber;
})();

module.exports = FlyweightNumber;

How This Works:

  • Fast Lookup with Map: We still use a regular Map to quickly find existing instances by their number—this keeps the flyweight pattern’s performance benefits.
  • Automatic Cleanup with FinalizationRegistry: When a FlyweightNumber instance is no longer referenced anywhere else in your code, the garbage collector will eventually collect it. At that point, the registry’s callback runs and removes the corresponding number entry from the instances Map.
  • Preserved Behavior: The toString, valueOf, and toJSON methods are kept intact so your flyweight instances behave just like regular numbers in most contexts.

Example Usage:

let numA = new FlyweightNumber(42);
let numB = new FlyweightNumber(42);
console.log(numA === numB); // true (same instance)

// Remove all external references
numA = null;
numB = null;

// After garbage collection runs, instances.get(42) will return undefined
// Creating a new FlyweightNumber(42) will generate a fresh instance

Why Not WeakMap Directly?

WeakMap requires objects as keys, so we can’t use primitive numbers to look up instances. The FinalizationRegistry workaround lets us keep the primitive number lookup while still enabling proper garbage collection.

This implementation works in all modern browsers and Node.js versions that support ES2021 or later.

内容的提问来源于stack exchange,提问作者Paulo Henrique

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:03:32