"var" declarations shadowing properties from Window.prototype

Kyle Huey mailed to public-webapps about a problem we have with 
unprefixing IndexedDB:

   http://lists.w3.org/Archives/Public/public-webapps/2012JulSep/0392.html

The summary is:

1. IndexedDB defines:

     interface IDBEnvironment {
       readonly attribute IDBFactory indexedDB;
     };
     Window implements IDBEnvironment;

2. People are trying to fall back to prefixed versions of IndexedDB by 
writing code like (hooray prefixes):

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

3. Web IDL says that the "indexedDB" property for the IDL attribute 
should live on Window.prototype.

4. Recent versions of the ECMAScript spec say that the var declaration 
first will create a property on the global object (i.e. window) with the 
value undefined, before performing the assignment.

5. This means that the "window.indexedDB" part of the expression on the 
RHS evaluates to undefined.

6. This means that we cannot remove our unprefixed version of IndexedDB 
at the same time as adding the prefixed version (which we want to do), 
because then the "indexedDB" variable will just be undefined.

7. Older versions of the ECMAScript spec did not say to create the 
shadowing variable, so only implementations that follow that change AND 
have implemented Web IDL's attributes-on-prototypes get tripped up by this.

8. But the same problem would exist with operations, where properties 
have always existed on the prototype.  For example if you wrote:

   var requestAnimationFrame = window.requestAnimationFrame ||
                               window.mozAnimationFrame || ...;

you have the same problem.


So what do we do about this, if we accept that this is a problem?

1. We could change the ECMAScript spec to not do this shadowing thing.

2. We could change Web IDL to place all properties for IDL attributes 
and operation on Window (and interfaces it inherits from or has mixed in 
to) as own properties on window itself, rather than on the prototype, 
AND give them all [Replaceable]-like semantics, where if you assign some 
value that doesn't make sense for an attribute you overwrite it and lose 
its special behaviour.  That would cause the var declaration, according 
to the ES spec, not to create a new property with value undefined.  But, 
it would make it harder to monkeypatch, say, 
EventTarget.prototype.addEventListener, since window isn't really 
inheriting it from EventTarget.prototype any more but having its own 
copy of it.

3. We could change Web IDL so that window's [[DefineOwnProperty]] 
refuses to create the shadowing undefined property if the property name 
matches one of the IDL attributes/operations on Window (or its 
ancestors, etc.)  That effectively works around the ES spec shadowing 
requirement for var statements without needing to "willfully violate" it.


With options (2) and (3), we are assuming that existing scripts do not 
rely on for example:

   <script>
     var open;
     assert(open === undefined);
   </script>

Boris thinks that this assumption is safe, but evidence to the contrary 
would be useful if someone has it.


Another thing to be careful of is:

   <script>
     function open() { }
     assert(open != Window.prototype.open);
   </script>

which I think scripts are doing (they want to call their own "open").

With (2) this keeps working, since the own property "open" on window 
exists (which means the var statement doesn't try to overwrite it with 
undefined) and the property is writable (so the assignment of the 
Function object works).

With (3) this also keeps working, since the [[DefineOwnProperty]] on 
window for "open" would be ignored, and the assignment of the Function 
object shadows the property from the prototype.


The upshot of (3) is that for IDL attributes like "onclick", doing

   var onclick = function() { ... };

will be just like assigning to window.onclick.  Whether that's desired, 
I'm not sure.


Input on what to do here, especially from TC39 folks, is very welcome.

Received on Friday, 10 August 2012 03:36:44 UTC