The global object as the "global scope instance object"

Andreas Rossberg rossberg at google.com
Mon Jan 23 05:38:09 PST 2012


On 21 January 2012 20:36, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
> On Jan 21, 2012, at 12:25 AM, Andreas Rossberg wrote:
>> On 21 January 2012 01:23, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
>>> Temporal dead-zone initialization tracking requires extra state and checking
>>> (except for the common cases within functions where it can be statically
>>> determined that there are no possible references within the dead zone).
>>>  This additional requirement is reflected in the specification in the ES5
>>> definition of the CreateImmutableBinding and InitializeImmutableBinding
>>> environment record methods.  In ES5 this mechanism (and state) was only
>>> needed within DeclarativeEnviornmentRecords for (the rarely used) immutable
>>> bindings. For the ES6 spec. this has been generalize to also apply to
>>> ObjectEnvironmentRecords and to track initialization of both mutable and
>>> immutable bindings.
>>
>> In my proposal, I think you wouldn't need this generalisation.
>> Uninitialised state needs to be tracked for environment bindings only,
>> not for objects. The indirection through accessors makes the latter
>> unnecessary.
>
> Yes, but you still have the state because you are creating an actual environment binding for the global variable.

Yet, it would be nicely encapsulated in the semantics of temporal dead
zone for variables, i.e. environment bindings. You don't need to
proliferate it into the semantics of objects and object properties,
where it does not belong. I think this is important (see also my reply
to Gavin).


> BTW, I tried to express you approach (as I understand it) as a desugaring,  Here it is
>
> <script>
> print(foo);
> let foo="initialized";
> </script>
>
> desugars into something like:
>
> <script>
> {  //need to wrap a block around entire top level
>   // declaration instantiation for global  let and const  binding in this program
>   const  __getter_foo = function() {return __foo};
>   const  __setter_foo = function(v) {__foo = v};
>   Object.defineOwnProperty(this,'foo', {get:__getter_foo, set: __setter_foo,enumerable: true, configurable: false});
>   //end of declaration instantiation
>   print(foo);  //this.foo get accessor, but __foo will be uninitialized so it will actually throw reference error (temporal dead zone
>   let __foo =  "initialized";  //declare and initialize backing store for global variable foo
> }
> <script>

Yes, except that the print would simply amount to print(__foo)
directly, not involving the global object at all. Just as in any other
scope.

And of course, any decent implementation would optimize these special
accessors, just as it will have to for modules, which introduce the
same kind of structure.


> I sounds to me like you are making an ease of implementation argument.  I'm saying that an implementor could hide your proposed mechanism or some other semantically equivalent mechanisms to just expose the mechanism.  No double that is true, but it isn't clear to me that hiding the mechanism would be impossible or even excessively burdensome. From a user perspective, it would certainly be better to have it be hidden.

Actually, I'm not making an implementation argument at all!
Implementations will want to optimize the accessors, which clearly is
work (though, again, work that has to be done for modules anyway).

I'm making a semantics argument. I claim that the accessor approach is
simpler and more regular. Simpler to spec, simpler to explain (at
least when it comes to the details).


>> In any case, we should do the same for the toplevel and for modules.
>
> I believe that the current plan intentionally requires different semantic for the top level and for the top interior level of modules.  I agree that there that a scheme that minimized the difference is desirable.  It isn't clear to me that eliminating all differences is possible given legacy top level semantics.

That may be true, but we should try.  (Not sure about the
"intentionally", though, other than that modules "intentionally" avoid
the issues with the current toplevel semantics.)


>>> <script>
>>> "do not use strict";
>>> //window.foo does not exist at this point.
>>> foo = 10
>>> const foo=5;  //or let
>>> print(foo);
>>> </script>
>>
>> The effect of this will be the same as in any other scope, e.g.:
>>
>> {
>>  foo = 10
>>  const foo=5;  //or let
>>  print(foo);
>> }
>>
>> I.e., the first assignment is an (early) error, because you can't
>> assign to a const. If it was a let, it would still be a temporal dead
>> zone violation. The global object doesn't enter the picture at all.
>
> I might agree, but first we also would have to consider what the semantics are if window.foo already existed prior to starting the script block and whether or not it is acceptable for breaking the script into two scripts (split after the foo=10 statement) produces a different result.

The properties on the global object would have to be defined before
the script starts. If they cannot be defined, that is an early error.

Splitting the script as you describe does not make sense in strict
mode, because the first half would not compile (early reference
error).

In classic mode (assuming we really take the pain of backporting
const), I assume it would work, because foo is still configurable when
you reach the second script.


>>> <script>
>>> //window.foo does not exist at this point.
>>> window.foo = 10
>>> const foo=5;  //or let
>>> print(foo);
>>> </script>
>>
>> This behaves the same as:
>>
>> module W {
>>  W.foo = 10
>>  const foo=5;
>>  print(foo);
>> }
>
> Is the above legal in the module system?  The implication is that every modules is reified as an object bound wthin its body and hence the use can use properties definitions (including assignment to non-existence properties) instead of explicit exports  to define the module export?

Yes, every module is reified to an instance object, and since modules
are recursive, this is available in its own body.

But no, the example is not legal, in the sense that it will throw a
runtime error, for the reason I described (there is no setter for foo;
with let, you'd get a deadzone violation).

Nor would it work to assign another property `bar' to W, for that
matter, because module instance objects are not extensible. As I said,
in that respect, they would differ from the toplevel object.


> I'm not at all sure that it would be wise to think of the global object as a module instance object.

You did not really tell why you think so. :)

/Andreas


More information about the es-discuss mailing list