The global object should not be the "global scope instance object"

Brendan Eich brendan at mozilla.org
Thu Jan 26 16:57:22 PST 2012


Allen Wirfs-Brock wrote:
> On Jan 26, 2012, at 11:51 AM, Brendan Eich wrote:
>> We could say event handler attributes are scoped by the most-nested 
>> scope -- the scope due to the last <script> element that was closed 
>> before the element with the event handler attribute was processed.
>> This means generated scripts (document.write or DOM create* calls) do 
>> not see the generating script's let and const bindings.
>
> Why closed? All declarations within a /Program/ are instantiated 
> before evaluating any code in its <script> element so generated 
> scripts should be able to reference the bindings for those 
> declarations.  However, if such reference are actually evaluated they 
> might fall into the bindings' temporal dead zones.

You're right, the entire outer <script> has been parsed and declarations 
processed before anything runs. And the script element is appended to 
the DOM too. It all works, except of course you can't tell what's 
potentially shadowing anything!


>> But it's worse than that. What about <script async src="..."> (or the 
>> HTML4-era, not fully supported on all browsers <scriptdefer 
>> src="...">? Async scripts are not supposed to have global effects 
>> other than defining functions that the page or app author ensures 
>> won't be called till after all content (or at least all scripts, or 
>> at least all of the needed async scripts) has finished loading.
>
> Is this a specified restriction?  If it is, I haven't been able to 
> find it anywhere.

No, this is underspecified and so inherently racy -- developers beware. 
It's advisory but people use onload, DOMContentReady, or better 
(http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#current-document-readiness).

>> In what scope do async script let/const top-level bindings go?
>
> If it is a specified restriction then I would think that  when a 
> browser ES implementation actually processes such scripts they should 
> reject any top level declarations other than functions.

No, vars must be accepted too.

> Regardless, it seems likely that indeterminate load ordering problems 
> created by  HTML need to be solved at that level.  Why don't script 
> elements have a dependency attribute:

Ok, are we really going to invent new <script> attributes or just solve 
the let/const problem as it exists today? Adding attributes doesn't help 
in the extant case.

> <script src="someLib.js"  type="application/javascript" async="async" 
> id="lib1"></script>
> <script src="anotherLib.js"  type="application/javascript" 
> async="async"id="lib2"></script>
> <script src="baseApp.js"  type="application/javascript" async="async" 
> id="base" after="lib1"></script>
> <script src="extraApp"  type="application/javascript" async="async" 
> after="lib2,base"></script>

Because that's brittle and slow -- you don't care about order, you 
typically just want to race and exploit as much parallelism as possible, 
given host CPU(s), the intervening network, TCP connection 
sharing/limits/etc.

>> The situation is messy enough that I question the win of nesting 
>> scopes. True, a global object subject to async-loaded effects is no 
>> picnic, but it is the devil we know. And these var and function 
>> bindings, however racy, are not supposed to be lexical, as let and 
>> const are.
>
> I think these are the alternatives we currently have under consideration:
>
> SQ (status quo):  every top level declaration are all implemented as 
> properties of the global object
> STL (Single Top Level):  All script blocks share a declarative 
> environment for new (not var and function) kinds of declarations that 
> shadows the global object. Scripts with duplicate declarations are 
> rejected.
> TLpSi (Top Level per script, isolated):  Each script block has a 
> declarative environment for new (not var and function) kinds of 
> declarations.  Each shadows the global object but are invisible to 
> each other.
> TLpSn (Top Level per script, nested):  Each script block has a 
> declarative environment for new (not var and function) kinds of 
> declarations.  Each is logically nest within and shadows the 
> previously processed script
>
> STL is what I'm proposing.

I'm pretty sure the collisions-are-rejected thing is going to burn. It's 
the opposite of what people not only abuse (unknown latent bugs), but 
absolutely use and rely on today, with var and function among scripts.

>  I think that TLpSn corresponds to  Andreas preference.  One way to 
> look at TLpSn is that uses shadowing to accept the duplicate 
> declarations that STL rejects.  This seems like a problem that is 
> better addressed by using modules. TLpBn

(s/B/S/ right?)

> also raises issues like, what scope does an indirect eval use?  I 
> haven't head anyone recently advocating for TLpSi.  You can achieve 
> almost the same thing using SQ by wrapping a block around each script 
> body.

Right, although TLpSi is still attractive as a parallel to function 
bodies, where we agreed body-level let has to bind in a body block that 
shadows parameters and vars. Not much of a plus but it's a plausible 
alternative.

> In general, when I start think about the ways that lexical 
> declarations in separate scripts might interact I run into issue that 
> are probably best resolved using modules. I'm inclined to favor the 
> simpler solution and leave it to modules to deal with managing actual 
> interdependencies cases.

Is STL the simplest solution for users?

/be


More information about the es-discuss mailing list