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