Object.unfreeze, or similar API
/#!/JoePea
joe at trusktr.io
Mon Feb 19 19:56:33 UTC 2018
I think the ability to unfreeze an object can be useful.
For example, suppose we have a library that emits events:
```js
this.emit('some:event', this.obj)
```
Maybe we want to try to prevent external listeners from modifying the event
payload without having to clone `this.obj`, otherwise cloning can cause
"jank" from GC during animations that should be smooth.
So, what if there was a way to unfreeze an object in the scope in which the
object was frozen?
```js
Object.freeze(obj) // instead of cloning
this.emit('some:event', this.obj) // recommend all listeners to be
synchronous, and not modify the payload later
Object.unfreeze(this.obj) // in the same scope
```
or maybe it return an unfreeze function:
```js
const unfreeze = Object.freeze(obj) // instead of cloning
this.emit('some:event', this.obj) // recommend all listeners to be
synchronous, and not modify the payload later
unfreeze(this.obj) // only code that is given the unfreeze function can
call it, similar to resolve/reject functions with Promises
```
or maybe it is more similar to setTimeout and setInterval:
```js
const frozenKey = Object.freeze(obj) // instead of cloning
this.emit('some:event', this.obj) // recommend all listeners to be
synchronous, and not modify the payload later
Object.unfreeze(frozenKey) // only code that is given the key can unfreeze
the object associated with the key
```
It seems like there can be opportunity for this to be optimized, so that it
can be faster than cloning. For example, this uses more memory and causes
GC:
```js
this.emit('some:event', { ...this.obj }) // creates a new object
```
Could the performance benefit make it worth it to have a way to unfreeze
objects?
Another use case is unlocking things only when used in certain places, as a
"guard". In the following example, the only way to set the X rotation value
is with the setter, which enforces that certain logic will fire, rather
than someone from the outside modifying the readable state not using the
setter:
```js
import Privates from 'somewhere'
// a private cache, using WeakMap internally
const _ = new Privates
class ThingThatRotates {
constructor() {
this.rotation = { x: 0, y: 0, z: 0 }
_(this).frozen = Object.freeze(this.rotation)
// ...
}
get rotateX() { return this.rotation.x }
set rotateX(val) {
this.doSomethingImportantWithTheValue(val)
// unlock here, so that the value can only be set with this setter.
Object.unfreeze(_(this).frozen)
this.rotation.x = val
_(this).frozen = Object.freeze(this.rotation)
this.emitAnEventWithTheValue(val)
this.etc(val)
}
// ...
}
```
I know, I can probably just keep `rotation` in the private cache, but then
it isn't readable. Maybe I want that to be readable as a convenient
shortcut. F.e.
```js
// outside code
const r = new ThingThatRotates
// X rotation might be animated by some other tool
const {x, y, z} = r.rotation // for convenience, no cloning involved.
```
This is a completely contrived example I just made up right now. I can
probably think of more. It seems like it could be good for performance in
cases where we don't want modification from the outside...
/#!/JoePea
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180219/2289f945/attachment.html>
More information about the es-discuss
mailing list