The global object as the "global scope instance object"

Allen Wirfs-Brock allen at wirfs-brock.com
Sat Jan 21 11:36:23 PST 2012


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.

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>

>>> ...
>>> This disallows lexical bindings to redefine existing non-configurable
>>> properties of the global object (including ones reflecting bindings
>>> from earlier scripts), which is checked before the script starts
>>> executing. Likewise, let/const-defined properties cannot later be
>>> deleted from the global object.
>> 
>> I'm not yet convinced that you need to publicly expose such global object
>> properties accessor properties.  I think it is possible to hide the
>> mechanisms.
> 
> I don't think so, at least not as cleanly.  With accessors, you avoid
> the whole notion of uninitialised object property, which is painful
> and pretty intrusive.

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.

> 
> 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.

> 
> 
>> Independent of implementation schemes, I think we need to decide on the
>> meaning of the following independent scripts (in different documents)
> 
> Those are the kinds of examples I was alluding to. And the main point
> of my proposal, and the use of accessors, is that those examples get a
> natural and coherent meaning, without requiring any special rules.
> Namely:

I just want to make sure we know what we want these to do, independent of implementation strategy,  I don't think we should be using accessors or any other implementation tricks in deciding the user facing 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.


> 
>> <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

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?


>  const foo=5;
>  print(foo);
> }
> 
> which in turn behaves like:
> 
> Object.defineProperty(window, "foo", {get: function() { return foo }})
> window.foo = 10
> const foo = 5
> 
> I.e., the assignment again is an error, this time because there is no
> setter for foo. If it was a let, then you'd have a temporal dead zone
> violation in the setter.
> 
>> <script>
>> //window.foo does not exist at this point.
>> print(foo);
>> const foo=5;  //or let
>> print(foo);
>> </script>
> 
> Again, same as in any other scope: temporal dead zone violation on the
> first print.
> 
> The idea is that the top level is just another declarative scope, and
> all rules regarding its reflection in the global object mirror the
> rules for module instance objects. Less rules, less pain. :)

I'm not at all sure that it would be wise to think of the global object as a module instance object.  I do agree that there is still some benefit and possibilities for using declarative scope semantics at the global level.


> ...
> 

Allen


More information about the es-discuss mailing list