# \`new WeakMap(entries, { properties: true });\`

**URL:** <https://es.discourse.group/t/new-weakmap-entries-properties-true/2645>\
**Category:** 💡 Ideas\
**Created:** [September 8, 2026, 1:02pm UTC](https://es.discourse.group/t/new-weakmap-entries-properties-true/2645 "2026-09-08T13:02:36Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [September 8, 2026, 1:02pm UTC](https://es.discourse.group/t/new-weakmap-entries-properties-true/2645/1 "2026-09-08T13:02:36Z")

</div>

This is an observation of the `WeakMap` API and my experiences using it. The core observation is: it was always possible for the engine to implement `WeakMap` in two different ways.

1. As hash map with weakly-held keys. (what engines do)
2. As hidden properties on objects.

This idea would allow the user to ask the engine for an instance of `WeakMap` backed by hidden properties on objects. The resulting `WeakMap` instance would be indistinguishable from a normal one at the API level.

This implies a new `weak` flag in each property descriptor, which would be necessary to prevent the weakmap's hidden property descriptors from becoming visible using reflection APIs. A `WeakMap` implemented this way would hold a Symbol which it would never expose through its publc APIs. This would be the key used in the `weak` property descriptor. Thus, only the holder of the weak map would be able to read and write this data. Since the symbol would be invisible to reflection, it would not be possible to gain access to it and forge it. The ability to read and write the hidden property could still be delegated using getter and setter functions.

Why allow this? Well, it's a performance optimization. The default implementation is safe to use freely because it doesn't alter any hidden classes. Altering the hidden classes can have disasterous implications for performance if it isn't done with intention and care. This is to say: I think it makes sense that the default implementation is what it is.

To make safe and proper use of a an implementation that alters the hidden class, you'd need to use it like this:

```js
let integritySet = new WeakSet([], { properties: true });

export class ClassWithIntegrity {
  constructor() {
    this.value = 'OK'; // hidden class altered on this line
    integritySet.add(this); // hidden class altered on this line
    Object.freeze(this);
  }
}

```

Now you can do exactly this with the current `WeakMap`, and it works -- as I said, the APIs are identical. Unfortunately, the perf characteristics are not identical. If your usage is modeled on the cost of hidden properties in still-monomorphic objects, you will be sorely disappointed by the cost of maintaining a hash map, especially when the number of objects get into the millions. Hidden properties on monomorphic objects are still effortless from a perf standpoint, even with millions of objects. Weak hash maps are very much not effortless, and begin to consume (very) large amounts of time in garbage collecting, resizing, and reallocating the mapping table.

So I believe then the incentives are created for this proposal: both implementations shine, for different perf-related reasons, so long as they are used the way they are designed to perform well.

In practice this lets us efficiently do "parse don't validate" style coding with classes. Previously there was no mechanism for creating verifiable integrity that scaled to millions of object instances and beyond.

```js
let integritySet = new WeakSet([], { properties: true });

export class ClassWithIntegrity {
  constructor() {
    // ...
  }

  logOK() {
    if (!integritySet.has(this)) throw new TypeError();
    console.log(this.value); // logs OK
  }
}

```

Here's another example of how you can use this to create data integrity, even when you're working with mutable data.

```js
let fooData = new WeakMap({ properties: true });
let barData = new WeakMap({ properties: true });

class DataAccessors {
  constructor() {
    fooData.set(this, 'foo');
    barData.set(this, 'bar');
  }
  
  get foo() { return fooData.get(this); }
  set foo(value) {
    if (!value.startsWith('foo')) throw new Error();
    return fooData.set(this, value);
  }

  get bar() { return barData.get(this); }
  set bar(value) { return barData.set(this, value); }
}

```

Once again you could do this with the kind of `WeakMap` we already have, but once again you'd be hitting the perf use case that suffers most: implementing what should be a fast monomorphic struct access operation with slow hash map code and its internal operations like rebucketing that sometimes stop the world.

A final thought: the integrity use case, to function correctly, requires that the secret symbol used as the property key remain a secret. Without an engine-level implementation of `propertyDescriptor.weak`, it would be difficult to keep the symbol a secret. Luckily if the engine does not natively support `properties: true`, it can and will simply fall back to the map-based implementation, which will again provide integrity to the extent that the engine offers it. This time though the fallback has very broad support, so basically you could have runtime-backed integrity across almost any runtime, with perf that improves rapidly as runtimes implement `properties: true`.

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [September 8, 2026, 1:33pm UTC](https://es.discourse.group/t/new-weakmap-entries-properties-true/2645/2 "2026-09-08T13:33:40Z")

</div>

Hmm I think the idea still isn't complete. What if one of the weak maps itself gets garbage collected? That's the real problem. Then to get back to the state you started with you'd have to go alter the prototypes of a bunch of live objects. That would not be good. That would be very slow.

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [September 8, 2026, 2:39pm UTC](https://es.discourse.group/t/new-weakmap-entries-properties-true/2645/3 "2026-09-08T14:39:34Z")

</div>

If you just let the map be collected though and leave the weak properties, I don't really see a problem with that though, so long as it's made very clear that that's the behavior

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [September 8, 2026, 9:45pm UTC](https://es.discourse.group/t/new-weakmap-entries-properties-true/2645/4 "2026-09-08T21:45:20Z")

</div>

Now that I think carefully about it, I think it's possible to polyfill this. The main thing is hiding the descriptors, which can be done by patching the implementations of `getOwnPropertyDescriptors()` and `getOwnPropertySymbols`. I would patch them to omit any symbol-keyed descriptor where symbol's `description` is `'$ __WEAK_KEY__'`. Then the only way to look up the data would be by knowing the symbol.

---

<div class="post-metadata">

**Author:** ![ljharb](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/ljharb/32/8_2.png) [@ljharb](https://es.discourse.group/u/ljharb)\
**Post date:** [September 8, 2026, 9:57pm UTC](https://es.discourse.group/t/new-weakmap-entries-properties-true/2645/5 "2026-09-08T21:57:11Z")

</div>

… or by grabbing one of those functions (assuming you also included Reflect.ownKeys) from another realm, which you can't patch.

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [September 8, 2026, 10:08pm UTC](https://es.discourse.group/t/new-weakmap-entries-properties-true/2645/6 "2026-09-08T22:08:42Z")

</div>

Yeah, I see what you're saying. So the polyfill wouldn't be bulletproof as security, but once engines implement it there would be no way to use this other-realm hack to evade the intent.

---

<div class="post-metadata">

**Author:** ![aclaymore](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/aclaymore/32/501_2.png) [@aclaymore](https://es.discourse.group/u/aclaymore)\
**Post date:** [September 9, 2026, 1:40am UTC](https://es.discourse.group/t/new-weakmap-entries-properties-true/2645/7 "2026-09-09T01:40:38Z")

</div>

The polyfill wouldn't be able to define the property on a non-extensible object.

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [September 9, 2026, 8:27am UTC](https://es.discourse.group/t/new-weakmap-entries-properties-true/2645/8 "2026-09-09T08:27:08Z")

</div>

I'm fine with it if the real implementation shares that behavior from the polyfill. It would indeed mean that freezing an object would prevent its weakmapped value(s) from changing further.

It would mean that the two WeakMap implementations aren't completely identical in API though. You could now feature detect one from the other by testing the behavior on frozen objects.

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [September 9, 2026, 9:58am UTC](https://es.discourse.group/t/new-weakmap-entries-properties-true/2645/9 "2026-09-09T09:58:40Z")

</div>

Also while I see the same transitional security concern regarding iframes that @ljharb sees, I see this change as a part of a complete vision that also eliminates the current DOM API and replaces it with a fully declarative DOM API. As long as those changes arrive together (and I see no reason that they should not) my understanding is that no insecurity would exist: the technique of using another realm's `Object` implementation requires access to the (old/current) DOM API. The new API does not replicate the same flaw.

I'm considering breaking changes because I'm laying the foundations for an operating system. If I should succeed in building an environment that other people find it to be worth writing code in, it won't be about what old code runs in the new environment, but rather about what the new environment makes possible that no past one did. In that context it's obvious why I want class integrity. In fact it's critical for the implementation of the new DOM API that such strong integrity exists: the new immutable API design is predicated on it.

...and then immutability is the key to being able to design a DOM API which can safely sync DOM changes between threads in a desktop application architecture where you have many application threads, each of which also own a subtree of the OS-level DOM.

---

<div class="post-metadata">

**Author:** ![jridgewell](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/jridgewell/32/18_2.png) [@jridgewell](https://es.discourse.group/u/jridgewell)\
**Post date:** [September 9, 2026, 7:21pm UTC](https://es.discourse.group/t/new-weakmap-entries-properties-true/2645/10 "2026-09-09T19:21:41Z")

</div>

You can implement this with Private Fields and return override trick:

```javascript
class Base {
  constructor(obj) {
    return obj;
  }
}

class WeakMap<K, V> {
  // Inner holds the private field, ensuring every new WeakMap instance gets
  // its own #key.
  #Inner = class extends Base {
    #key;

    static get(object: K): V | undefined {
      this.stamp(object);
      return object.#key;
    }

    static set(object: K, value: V): void {
      this.stamp(object);
      object.#key = value;
    }

    static stamp(object: K): void {
      if (#key in object) return;
      new this(object);
    }
  }

  get(object: K): V | undefined {
    return this.#Inner.get(object);
  }

  set(object: K, value: V): void {
    this.#Inner.set(object, value);
  }
}

```

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [September 9, 2026, 7:48pm UTC](https://es.discourse.group/t/new-weakmap-entries-properties-true/2645/11 "2026-09-09T19:48:05Z")

</div>

That's awfully clever! ...which makes me uncomfortable... ...but also it would seem to be able to provide the needed mechanism without the defects that my proposed polyfill would involve.

I like the idea that the complicated return-override-with-private-property machinery is encapsulated by a simple WeakMap-like API.

---

<div class="post-metadata">

**Author:** ![mhofman](https://avatars.discourse-cdn.com/v4/letter/m/f14d63/32.png) [@mhofman](https://es.discourse.group/u/mhofman)\
**Post date:** [September 10, 2026, 1:53pm UTC](https://es.discourse.group/t/new-weakmap-entries-properties-true/2645/12 "2026-09-10T13:53:15Z")

</div>

> [@conartist6](#):
>
> the complicated return-override-with-private-property machinery is encapsulated by a simple WeakMap-like API.

> [@conartist6](#):
>
> without the defects that my proposed polyfill would involve.

Please do not rely on this trick if you expect all objects to be usable as keys. The DOM already has affordances to disallow any object it wants (I believe only `window` currently), and with [GitHub - tc39/proposal-nonextensible-applies-to-private: Proposal to disallow adding new private fields to non-extensible objects · GitHub](https://github.com/tc39/proposal-nonextensible-applies-to-private) at stage 3, expect non-extensible objects to also be disallowed soon in most engines.

> [@conartist6](#):
>
> The core observation is: it was always possible for the engine to implement `WeakMap` in two different ways.
> 
> 1. As hash map with weakly-held keys. (what engines do)
> 2. As hidden properties on objects.

There is actually a 3rd "hybrid" way, which is used by engines like Moddable XS: a hashmap to a "joiner slot", which is also linked from the object's "slot list". It avoids the ephemeron complexity of the normal hashmap approach.

> [@conartist6](#):
>
> Why allow this? Well, it's a performance optimization.

In JS performance optimizations rarely justify a change in API, especially exposing imperative knobs to the author, because more often than not, the author will make the wrong assumptions.

Instead JS provides alternative constructs which imply different semantics. That's actually the case here with your examples: they all seem to assume the object with hidden data is `this` and you already rely on `class` syntax.

If that's the case, why don't you use normal private fields in your class definition? Do you actually need the `WeakMap` semantics of attaching private data to objects you didn't create?

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [September 10, 2026, 3:25pm UTC](https://es.discourse.group/t/new-weakmap-entries-properties-true/2645/13 "2026-09-10T15:25:59Z")

</div>

Ah yes. What I'm not showing you is that in my real code I'm not using classes, I'm writing in the functional style using null-prototype objects and arrays as my core data types. Most instances are `{ type, value }`.

As I say I'm not looking to use this to extend arbitrary objects, only to put an integrity seal on certain kinds.

You're suggesting, I guess, that I do it like this:

```js
class ClassWithIntegrity {
  # __proto__ = Object.getPrototypeOf(this);
}

```

I'm asking... why! This just seems crazy to use the language's own prototype feature and then have to reinvent it on top of itself.

---

<div class="post-metadata">

**Author:** ![mhofman](https://avatars.discourse-cdn.com/v4/letter/m/f14d63/32.png) [@mhofman](https://es.discourse.group/u/mhofman)\
**Post date:** [September 10, 2026, 4:18pm UTC](https://es.discourse.group/t/new-weakmap-entries-properties-true/2645/14 "2026-09-10T16:18:57Z")

</div>

> [@conartist6](#):
>
> I'm writing in the functional style using null-prototype objects and arrays as my core data types.

It would be better if you showed example like that. If you're avoiding classes but creating your own objects, it's possible [private declarations](https://github.com/tc39/proposal-private-declarations) would be sufficient for you? In particular the "Non-class object factories" example at the end.

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [September 10, 2026, 4:27pm UTC](https://es.discourse.group/t/new-weakmap-entries-properties-true/2645/15 "2026-09-10T16:27:30Z")

</div>

Will you please share a concrete example of how you would write class code with integrity in your preferred idiom? By not sharing code you're avoiding showing me how you would handle crucial real details.

---

<div class="post-metadata">

**Author:** ![mhofman](https://avatars.discourse-cdn.com/v4/letter/m/f14d63/32.png) [@mhofman](https://es.discourse.group/u/mhofman)\
**Post date:** [September 10, 2026, 6:02pm UTC](https://es.discourse.group/t/new-weakmap-entries-properties-true/2645/16 "2026-09-10T18:02:27Z")

</div>

I thought this was pretty normal branding with class private fields:

```js
class ClassWithIntegrity {
  #brand;

  constructor() {
    this.value = 'OK';
    Object.freeze(this);
  }

  logOK() {
    if (!(#brand in this)) throw new TypeError();
    console.log(this.value); // logs OK
  }
}

```

or probably more simply:

```javascript
class ClassWithIntegrity {
  #value;

  constructor() {
    this.#value = 'OK';
    Object.freeze(this);
  }

  logOK() {
    console.log(this.#value); // logs OK
  }
}

```

or for the accessor example:

```js
class DataAccessors {
  #foo;
  #bar;

  constructor() {
    this.#foo = 'foo';
    this.#bar = 'bar';
    Object.freeze(this); // added this to show freezing doesn't affect private fields
  }
  
  get foo() { return this.#foo; }
  set foo(value) {
    this.#foo = value;
  }

  get bar() { return this.#bar; }
  set bar(value) { this.#bar = value; }
}

```

And for private declarations:

```js
private #foo;
const makeObj = (foo) => Object.freeze({
  __proto__ : null,
  outer #foo: foo,
});

```
