Summary
Introduce Ember.WeakMap
(@ember/weakmap
), an ES6 enspired WeakMap. A
WeakMap provides a mechanism for storing and retriving private state. The
WeakMap itself does not retain a reference to the state, allowing the state to
be reclaimed when the key is reclaimed.
A traditional WeakMap (and the one that will be part of the language) allows for weakness from key -> map, and also from map -> key. This allows either the Map, or the key being reclaimed to also release the state.
Unforunately, this bi-directional weakness is problemative to polyfil. Luckily, uni-directional weakness, in either direction, "just works". A polyfil must just choose a direction.
Note: Just like ES2015 WeakMap, only non null Objects can be used as keys
Note: Ember.WeakMap
can be used interchangibly with the ES2015 WeakMap. This
will allow us to eventually cut over entirely to the Native WeakMap.
Motivation
It is a common pattern to want to store private state about a specific object. When one stores this private state off-object, it can be tricky to understand when to release the state. When one stores this state on-object, it will be released when the object is released. Unfortunately, storing the state on-object without poluting the object itself is non-obvious.
As it turns out, Ember's Meta already solves this problem for
listeners/caches/chains/descriptors etc. Unfortunately today, there is no
public API for apps or addons to utilize this. Ember.WeakMap
aims to be
exactly that API.
Some examples:
- https://github.com/offirgolan/ember-cp-validations/blob/master/addon/utils/cycle-breaker.js
- https://github.com/stefanpenner/ember-state-services/ (will soon utilize the user-land polyfil of this) to prevent common leaks.
Detailed design
Public API
import WeakMap from '@ember/weak-map'
var private = new WeakMap();
var object = {};
var otherObject = {};
private.set(object, {
id: 1,
name: 'My File',
progress: 0
}) === private;
private.get(object) === {
id: 1,
name: 'My File',
progress: 0
});
private.has(object) === true;
private.has(otherObject) === false;
private.delete(object) === private;
private.has(object) === false;
Implementation Details
The backing store for Ember.WeakMap
will reside in a lazy ownMap
named
weak
on the key objects __meta__
object.
Each WeakMap
has its own internal GUID, which will be the name of its slot,
in the key objects meta weak bucket. This will allow one object to belong in
multiple weakMaps without chance of collision.
Concrete Implementation: https://github.com/emberjs/ember.js/pull/12224 Polyfill: https://www.npmjs.com/package/ember-weakmap
Drawbacks
- implementing bi-direction Weakness in userland is problematic.
- Using WeakMap will insert a non-enumerable
meta
onto the key Object.
Alternatives
- Weakness could be implemented in the other direction, but this has questionable utility.
Unresolved questions
N/A