Unscopeable

Erik Arvidsson erik.arvidsson at gmail.com
Wed Aug 21 06:41:39 PDT 2013


Some answers inline.

On Wed, Aug 21, 2013 at 7:58 AM, Andreas Rossberg <rossberg at google.com> wrote:
> I'm still trying to wrap my head around the idea of introducing an
> @@unscopeable property, as discussed at the last meeting. There seem
> to be plenty of edge cases:
>
> * How would it interact with inheritance?

with always uses the full prototype chain. @@unscopable is gotten
using a [[Get]] so the whole prototype chain is considered.

>   let p = {a: 1}
>   let o = {__proto__: p, [@@unscopeable]: ['a']}
>   let a = 2
>   with (o) {
>     a  // 1 or 2?
>   }

2, 'a' is blacklisted from being part of the object environment record.

>   let p = {[@@unscopeable]: ['a']}
>   let o = {__proto__: p, a: 1}
>   let a = 2
>   with (o) {
>     a  // 1 or 2?
>   }

2, for the same reason

> * How would it affect assignment?

This is one of the details we didn't talk about, but the concept is
that 'a' is not part of the object environment record, so free a means
the lexical a (if any).

One way to think of it is that the with is acting on a view of the
object that does not have the blacklisted properties. This can be
implemented using proxies. I'll leave that as an exercise.

>   let o = {[@@unscopeable]: ['a']}
>   let a = 2
>   with (o) {
>     a = 1
>     a  // 1 or 2?

1

>     o.a  // 1 or undefined?

undefined

>   }
>   a  // 1 or 2?

1

>   let p = {a: 1, [@@unscopeable]: ['a']}
>   let o = {__proto__: p}
>   let a = 2
>   with (o) {
>     a = 3
>     a  // 1 or 2 or 3?

3

>     o.a  // 1 or 3?

1

>   }
>   a  // 2 or 3?

3

> * How would it interact with mutation?

Another hole we didn't cover. Here is my proposal:

The @@unscopable is only accessed once, when the object environment
record is created.

>   let o = {a: 1}
>   let a = 2
>   with (o) {
>     o[@@unscopeable] = ['a']
>     a  // 1 or 2?

1, @@unscopable was set too late.

>   }
>
> * How would it interact with compound assignment?

this one falls out of the previous one.

>   let o = {a: 1}
>   let a = 2
>   with (o) {
>     a += (o[@@unscopeable] = ['a'], 2)
>     a  // 2 or 3 or 4?

3

>     o.a  // 1 or 3?

3

>   }
>   a  // 2 or 3 or 4?

2

> * How would it affect the global object? (At least currently, with
> scopes and the global scope share the same object environment
> mechanism.)

The idea was that it would be part of with only. We don't want to tax
the global property lookups for this.

>   var a = 1
>   this[@@unscopeable] = ['a']
>   a  // 1 or undefined?

1

>   var a
>   this[@@unscopeable] = ['a']
>   a = 1
>   a  // 1 or undefined?

1

>   this[@@unscopeable] = ['a']
>   </script><script>
>   var a = 1
>   a  // 1 or undefined?

1

> * How would it expose side effects?

It does a [[Get]] in the with head.

>   let s = ""
>   let o = {a: 1}
>   let u = {get '0'() { s+="0"; return 'a' }, get '1'() { s+="1"; return 'b' }}
>   Object.defineProperty(o, @@unscopeable, {get: function() { s += "u";
> return u }})
>
>   with (o) {}
>   s  // "" or "u" or "u01" or ...?
>
>   with (o) { c; c }
>   s  // "u01" or "u01u01" or ...?
>
>   with (o) { a }
>   s  // "u01" or "u0" or ...?
>
>   with (o) { a+=1 }
>   s  // "u0" or "u00" or "u01" or "u0101" or ...?
>
> Some of this looks rather whacky, if not unpleasant.

If you think of it as only doing it once in the with head things
become much simpler to understand.

-- 
erik


More information about the es-discuss mailing list