Object.unfreeze, or similar API

/#!/JoePea joe at trusktr.io
Mon Feb 19 20:10:25 UTC 2018


That's actually very useful, thanks! I have plenty of cases where I wished
I could pass an object without cloning it to ensure it isn't mutated from
outside. And instead of creating the Proxy right when I will emit an event
(for example), I could just store the proxy somewhere so it is long-lived
and to avoid GC.

*/#!/*JoePea

On Mon, Feb 19, 2018 at 12:08 PM, Michał Wadas <michalwadas at gmail.com>
wrote:

> You can just do:
>
> const proxy = new Proxy(obj, {
>    set() { throw new Error(); },
>    defineProperty() { throw new Error();},
>    deleteProperty() { throw new Error(); }
> })
> this.emit('some:event', proxy)
>
>
> Though, it seems like an exotic use case.
>
> On Mon, Feb 19, 2018 at 8:56 PM, /#!/JoePea <joe at trusktr.io> wrote:
>
>>>> 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
>>
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180219/b1d2bbc3/attachment-0001.html>


More information about the es-discuss mailing list