W3C home > Mailing lists > Public > public-script-coord@w3.org > July to September 2012

Re: "var" declarations shadowing properties from Window.prototype

From: Brendan Eich <brendan@mozilla.org>
Date: Fri, 10 Aug 2012 23:14:01 -0700
Message-ID: <5025F829.2030107@mozilla.org>
To: Allen Wirfs-Brock <allen@wirfs-brock.com>
CC: Boris Zbarsky <bzbarsky@MIT.EDU>, public-script-coord@w3.org, es-discuss@mozilla.org
Allen Wirfs-Brock wrote:
> On Aug 10, 2012, at 3:25 PM, Brendan Eich wrote:
>>> 4) The declaration instantiation rules relating to pre-existing bindings
>>> only consider own properties of the global object.  Inherited 
>>> properties of the
>>> global object have no effect upon the processing of function and var
>>> declarations.
>>
>> This is the incompatible change from ES1-5.1 and reality that I 
>> question whether we can get away with.
>
> True, for var declarations.  For function declarations it changed in 
> 5.1 as a result of 
> https://bugzilla.mozilla.org/show_bug.cgi?id=577325 which initially 
> concerned what happens with a global function declaration when there 
> is an inherited access with the same name.  Is the inherited setter 
> called?  We all concluded that it shouldn't. Rule 4 above is 
> essentially an expression of that idea.

Yes, I remember. 'function' always differed from 'var' in JS: it would 
create the binding (or throw trying, ES5+).

> Note this is a real world situation as Jonas notes in 
> https://bugzilla.mozilla.org/show_bug.cgi?id=781739#c9 :
>> Since we are on the subject, a similar thing which have been breaking 
>> in Firefox but working in Chrome is code which does:
>>
>> function onmessage(event) { ... }
>>
>> in global scope in workers.
>>
>> In Firefox the global scope object has on its prototype chain a 
>> setter for the 'onmessage' property. However this setter isn't run 
>> and instead a shadowing variable is declared.
>>
>> In Chrome the global scope object has the setter on the object 
>> itself, causing the setter to run.
>>
>> This caused the page to work in Chrome since the setter is run and 
>> thus an event handler was registered, while in Firefox a "expando" 
>> variable is created and nothing else happens.

Yeah, once more Chrome scores.

In primordial JS, writing function onload(){...} defined an onload 
handler. WYSIWYG and Occam's razor (perhaps too close a shave).

> In this case, firing the setter is perhaps what the programmer wanted, 
> even if it is a terrible way to accomplish that end.

It's not that bad if you start from the DOM level 0, especially 
window.onload being the same binding as function onload() {}.

>  However, the opposite could easily be true.  The programmer has a 
> working program with a function declaration for Foo.  Sometime latter 
> the browser adds an unrelated accessor property coincidentally named 
> Foo to window's prototype.  The program stops working when the 
> declaration doesn't over-ride the inherited property but instead calls 
> some setter with the wrong shaped function..

Yup. The "on" convention I borrowed from HyperCard, with all lowercase, 
helps avoid collisions for event handlers. Convention helps, but it's 
not enough.

OTOH the real challenge is not functions (Chrome got it right, I think). 
The issue is 'var' not creating a shadowing property, or else working 
because WebIDL does something different from its current spec.

>> You didn't give motivation for it. Obviously the motivation involves 
>> not wanting var declarations to be trumped by 
>> non-writable/non-configurable properties of the global object's 
>> prototype or grand-proto, etc. But do we have such properties?
>
> It started with functions declarations as per bug 577325 between ES5 
> and ES5.1.  Post ES5 the var issue came up as 
> https://bugs.ecmascript.org/show_bug.cgi?id=78 The attribute sniffing 
> logic in the ES5.1 change was about trying to identify properties that 
> have the characteristics of those created by function declarations 
> (which should be overwriteable by subsequent declarations) from those 
> that don't.
>
>>
>> One solution is to say that global proto-properties cannot be 
>> non-writable. I think that's an effective compatibility constraint 
>> already.
> By restricting [[DefineOwnProperty]] on proto-prototypes??

No, just by convention. Jonas points out that

  var indexedDB = window.mozIndexedDB || ... || window.indexedDB;

still will fail if the engine does not create a shadowing var, because 
indexedDB is a get-only accessor so the assignment in the initialiser 
will fail for want of set.

>> ES5 made the "own" property from ES3, undefined AKA this.undefined in 
>> global code, non-writable and non-configurable, but we have separate 
>> logic to allow 'var undefined;' (which is all over the web). Please 
>> correct me if I'm mistaken here. This is a different case, because 
>> "own" and not involving the prototype chain.
>
> I don't think there is anything special about the global named 
> undefined.  As long as it is a own property of the global object 
>  (which is is spec'ed to be) 'var undefined' is fine because redundant 
> var declarations don't do anything anything.  If undefined was 
> inherited from window.prototype it would be a different story.

Agreed, just noting that const x = 42; var x; is not tolerated in ES6 as 
proposed (right?).

>>> Supporting requirements 3&4 are where the "own" property checks were 
>>> introduced.
>>
>> I don't see 3, first sentence, as novel or at issue. If (and only if) 
>> a new property is bound by var, it will be "own". And function always 
>> blows away any prior configurable, etc. (10.5 step 5(e)), binding.
>
> What you just said about functions is how we justified over-riding 
> inherited function valued properties.

Yes. 'function' and 'var' differ. I think we agree -- I hope you're not 
trying to make 'var' more like 'function'.

> But what about var.  Rule 3 also means that 'var foo' guarantees you a 
> own property of the global object.  Otherwise we have the var analog 
> of the the above function Foo issue.

That is not a problem so much in practice, until recently :-|.

> How do we know whether 'var onClick = null' is  intended to call an 
> inherited setter or to create and initialize a global object property. 
>  Since there is an explicit 'var' that latter seems like a reasonable 
> guess.

LOL, the capital C means you are not touching a window.onclick handler. 
Plus, no such event handler on window. But you're right, we can't have 
it both ways. This strongly argues for global WebIDL-inherited 
properties being "own".

I see Jonas posted a followup making the same observation.

/be
Received on Saturday, 11 August 2012 06:14:34 UTC

This archive was generated by hypermail 2.3.1 : Wednesday, 8 May 2013 19:30:06 UTC