"var" declarations shadowing properties from Window.prototype

Brendan Eich brendan at mozilla.org
Fri Aug 10 15:25:28 PDT 2012

First, apologies for my posts based on partial knowledge -- not that 
I'll ever have total knowledge, but clearly a lot of people were 
confused about what ES5 and 5.1 said (me included!) vs. the post-ES5.1 
erratum that engines implemented. It did not help that the WebKit IDL 
binding machinery introduced a mitigating effect, IDL attributes as 
"own" global properties, which benefited only some browsers.

Vendor prefixes still suck, btw. :-|

Onward to what we might do:

Allen Wirfs-Brock wrote:
> These post ES5.1 errata was based upon these  desired semantics:
> 1) "variable" accesses that bind to inherited properties of the global 
> object
> should return the current value of the inherited property.  (note such 
> "variable" accesses
> may be to properties created by function declarations)

You mean unqualified Identifier expressions here, right? Of course, this 
cannot change, it goes back to the dawn of JS and would break the web if 
we lost it.

> 2) "variable" assignments  to inherited properties of the global 
> object should
> be equivalent to a [[Put]] to the global object.  Whether or not a own 
> property
> is created depends upon the [[Writable]] attribute of the inherited 
> property
> and the extensible internal property of the global object.

Yes, same as with any object.

> 3) global function and var declarations always create own properties 
> of the
> global object.

So far so good.

>  If an inherited property of the same name already exists it is
> over-ridden with an own property.

Isn't this sentence really 4, below?

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

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?

One solution is to say that global proto-properties cannot be 
non-writable. I think that's an effective compatibility constraint already.

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.

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

> However, I don't thing we can just drop them without some careful thought.

Always think carefullly. But also think about this: we have shipped 4 
and it is hurting.

>  If we did that, we would reintroduce problems related to global 
> declarations firing inherited setters and also interactions between 
> inherited property attributes and global declarations.

This is two-edged. The object detection code that people copied for 
indexedDB, combined with WebIDL and the IndexedDB spec based on WebIDL, 
*wants* inherited properties to prevent var from shadowing:

var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB || ...

This isn't the last such case of coincident naming derived from 
vendor-prefixed names.

> It feel like we are still playing semantics ping-pong with compat. bug 
> paddles. I'm not sure that there is an ideal solution.

On the Web, compat matters more. We had problems with var being 
sensitive to the prototype chain, particularly with proxies prototyped 
in SpiderMonkey and mutable __proto__. These were arguably 
implementation bugs, though.

Compat vs. implementation, compat wins. Rock smashes scissors.

Setters predate ES5 and so do host-object readonly properties. I do not 
believe these were a problem for the 15+ years before engines tried 
switching to get ahead of ES5.1 by fixing 
https://bugs.ecmascript.org/show_bug.cgi?id=78 and shadowing 
proto-properties with var.

Engines really all did, until very recently (and some popular ones may 
still do), refuse to shadow when var restates a proto-property by name.

> Perhaps we need to consider var and function declarations separately. 
>  Perhaps we need be have different redeclaration rules for own and for 
> inherited global object properties.  In any chase I think we need to 
> do a more care analysis then just fixing this bug and it needs be be 
> coordinated between the ES spec. and WebIDL.

Agreed, but we need to avoid delays and compounding hacks. Cameron's 
solution (3), having the global [[DefineOwnProperty]] check the global's 
proto chain, is interesting. It could be layered on top of the compat 
break wanted by https://bugs.ecmascript.org/show_bug.cgi?id=78. But is 
this playing Jenga? I smell it.

I think we might be better off looking again at the global proto-chain 
proxy bugs, which were more implementation than spec but also partly 
"spec", that we suffered, and seeing if they shouldn't be constrained or 
ruled out.


More information about the es-discuss mailing list