StoreData with Change Listeners
Implement StoreData, a Backbone.Model-style key/value store with change listeners.
store.add(key, value) // alias: set. Returns the store (chainable). store.get(key) // current value, or undefined store.has(key) // true while the key holds a value store.remove(key) // alias: unset. Soft-delete; true if something was removed store.on(event, cb) // register a listener; returns an unsubscribe function store.once(event, cb) // fires at most once store.off(event, cb) // remove one listener (all for the event when cb is omitted) store.toJSON() // plain object of the live keys
Events
"change:name"and plain"name"mean the same thing: the keynamechanged."change"is global — fires for every key that changes."unset"fires when a key is removed.- Every callback receives
(oldValue, newValue, key); a removal reportsnewValueasundefined.
Rules
- A set that doesn't change the value (
Object.is) fires nothing. - Key listeners fire before global listeners.
- One throwing listener must not stop the others.
- Removal is a soft delete:
hasreports false andgetreturnsundefined, but keep the tombstone.
Hints
Hint 1
Two Maps: attributes (key → {value, deleted}) and listeners (eventKey → Set of callbacks). Normalize every accepted event spelling to one internal key in a single place.
Hint 2
Namespace attribute events (attr:name) so a key literally called "change" can't collide with the global event — getting that normalization wrong is the classic bug here.
Hint 3
When firing, iterate over a copy of the Set and wrap each call in try/catch: a listener may unsubscribe another mid-loop, and one that throws must not silence the rest.