From what I understand, both - `window.x` and `x` should resolve to the same Reference, with base object being Global object and property name being "x". Both expressions - `delete x` and `delete window.x` - should evaluate to `false`, since `x` property of Global Object has {DontEnum} (due to that property created via variable declaration).
What I don't understand is why IE throws error in a latter case.
If `delete` is being passed a Reference with the same base object and same property name, why does it throw error in `delete window.x` case?
I suspect that the explanation is in identifier resolution process (10.1.4) and that base objects of References acquired as a result of evaluating `x` and `window.x` are not really the same objects.
Looking at `delete` algorithm (specified in 11.4.1)
UnaryExpression: delete UnaryExpression
1. Evaluate UnaryExpression. 2. If Type(Result(1)) is not Reference, return true. 3. Call GetBase(Result(1)). 4. Call GetPropertyName(Result(1)). 5. Call the [[Delete]] method on Result(3), providing Result(4) as the property name to delete. 6. Return Result(5).
Step 1 should evaluate UnaryExpression - `x` Identifier in this case. `x` is evaluated based on identifier resolution rules (10.1.4)
1. Get the next object in the scope chain. If there isn't one, go to step 5. 2. Call the [[HasProperty]] method of Result(1), passing the Identifier as the property name. 3. If Result(2) is true, return a value of type Reference whose base object is Result(1) and whose property name is the Identifier. 4. Go to step 1. 5. Return a value of type Reference whose base object is null and whose property name is the Identifier.
When run in global scope, step 1 results in global object being found as next object in scope chain (unless there's another object injected before Global object (?)). Step 2 calls [[HasProperty]] of global object with "x" as a property name and evaluates to `true`. Step 3 returns Reference with 'base' object being Global object and property name being - "x".
`delete` expression procedes with step 2 -
2. If Type(Result(1)) is not Reference, return true.
- but moves on to step 3, since `x` is in fact a Reference -
3. Call GetBase(Result(1)).
- which results in Global object -
4. Call GetPropertyName(Result(1)).
- which results in "x" string -
5. Call the [[Delete]] method on Result(3), providing Result(4) as the property name to delete.
- which calls [[Delete]] on Global with "x" as a property name; and -
6. Return Result(5).
- returning result.
Step 5 should, of course, result in `false` value, since Global object's "x" property has {DontDelete} (due to "x" property being created via variable declaration), but that is irrelevant.
Why does `window`'s [[Delete]] throw error when asked to delete an existent property (albeit with {DontEnum}) instead of returning `false`? Which object ends up being base of `x` Reference in `delete x;` and why does [[Delete]] on that object works as expected (contrary to behavior of base object of `window.x` Reference)?
And finally, why does -
window.window == window; // true, but delete window.window.x;
- result in a completely different - "Object doesn't support this action" - error?
> From what I understand, both - `window.x` and `x` should resolve to the > same Reference, with base object being Global object and property name > being "x". Both expressions - `delete x` and `delete window.x` - should > evaluate to `false`, since `x` property of Global Object has {DontEnum} > (due to that property created via variable declaration).
DontEnum or DontDelete?
DontDelete means that it is not enumerable on the variable object. What makes - window.x - not deletable is that it is done in a variable declaration.
var x; // var makes a DontDelete binding this.z = 1; // empty attributes
> What I don't understand is why IE throws error in a latter case.
> If `delete` is being passed a Reference with the same base object and > same property name, why does it throw error in `delete window.x` case?
> I suspect that the explanation is in identifier resolution process > (10.1.4) and that base objects of References acquired as a result of > evaluating `x` and `window.x` are not really the same objects.
Yes, that is somewhat explained in the Eric Lippert blog entry.
There is a bug in Firefox 2/Seamonkey where assiging to - window.x - will wipe off the DontDelete attribute, making the - x - property deletable.
javascript: var isIn = 'foo' in self; var foo = 42; foo = 43;alert([isIn, delete self.foo, self.foo]);
Apparently due to the bindings of inner and outer window objects, assigning to a property of window removes the DontDelete. This is something that came up in the GlobalScopePolluter discussion and I still don't understand that well.
IE is obviously incorrect, too. Witness the freakshow of trying to get the - name - property off the error that thrown by IE:-
javascript: var isIn = 'foo' in self; var foo = 42; alert(isIn); try{ delete self.p } catch(ex) { try { alert(ex.name); } catch(exex) { alert("can't get error.name: " + exex.message); }}
IE7: "can't get error.name: bad variable type"
AIUI, enumerating over IE's global object, a host object, misses enumerating over IE's global variable object.
> Looking at `delete` algorithm (specified in 11.4.1)
> UnaryExpression: delete UnaryExpression
> 1. Evaluate UnaryExpression. > 2. If Type(Result(1)) is not Reference, return true. > 3. Call GetBase(Result(1)). > 4. Call GetPropertyName(Result(1)). > 5. Call the [[Delete]] method on Result(3), providing Result(4) as the > property name to delete. > 6. Return Result(5).
> Step 1 should evaluate UnaryExpression - `x` Identifier in this case. > `x` is evaluated based on identifier resolution rules (10.1.4)
> 1. Get the next object in the scope chain. If there isn't one, go to > step 5. > 2. Call the [[HasProperty]] method of Result(1), passing the > Identifier as the property name. > 3. If Result(2) is true, return a value of type Reference whose base > object is Result(1) and whose property name is the Identifier. > 4. Go to step 1. > 5. Return a value of type Reference whose base object is null and > whose property name is the Identifier.
> When run in global scope, step 1 results in global object being found as > next object in scope chain (unless there's another object injected > before Global object (?)). Step 2 calls [[HasProperty]] of global object > with "x" as a property name and evaluates to `true`. Step 3 returns > Reference with 'base' object being Global object and property name being > - "x".
The global variable object is a different object in IE. There's no way to get a reference to a variable object.
[snip delete steps 1-4]
> 5. Call the [[Delete]] method on Result(3), providing Result(4) as the > property name to delete.
> - which calls [[Delete]] on Global with "x" as a property name; and -
> 6. Return Result(5).
> - returning result.
> Step 5 should, of course, result in `false` value, since Global object's > "x" property has {DontDelete} (due to "x" property being created via > variable declaration), but that is irrelevant.
It is relevant to the operation, but not related to the error thrown in IE.
It doesn't matter what is attempted to be deleted, and error is thrown always:-
javascript: alert(delete window.undef)
IE7: Error
> Why does `window`'s [[Delete]] throw error when asked to delete an > existent property (albeit with {DontEnum}) instead of returning `false`?
You seem to be using DontEnum where DontDelete makes sense.
var makes a DontDelete binding. Why calling delete on window throws in IE has to do with IE's window being a host object. I can't explain more than that.
> Which object ends up being base of `x` Reference in `delete x;` and why > does [[Delete]] on that object works as expected (contrary to behavior > of base object of `window.x` Reference)?
> And finally, why does -
> window.window == window; // true, but > delete window.window.x;
> - result in a completely different - "Object doesn't support this > action" - error?
IE gets the window property which points to the Host object which can't delete the variable - x -. Why? I don't know.
for(var v in window) - will fail to enumerate global properties in IE, even though the property is "in" window.
javascript: void(self.XXXXX = 12); var r = [], hap = ({}).hasOwnProperty; for(var VVVVVV in self) r.push(VVVVVV); alert([/X{5}/g.exec(r),/V{5}/g.exec(r), "VVVVVV" in self, hap.call(self, "XXXXX"), hap.call(self, "VVVVVV")] );
IE7: XXXXX,,true,true,true FF3.5, Safari 4, Seamonkey: XXXXX,VVVVVV,true,true,true Opera 10: XXXXX,VVVVVV,true,false,false
So, in IE, the variable VVVVVV is not going to show up in a for-in loop, though the property assigned will. The - in - operator and hasOwnProperty say "VVVVVV" is a property of self.
In Opera 10, the variables VVVVV and XXXXX are not properties of - self - and unsurprisingly, are not *own* properties of self.
I cannot conclude much about IE's implementation of window and so I've posted this message to m.p.s.jscript in hopes that someone there can explain better.
Garrett Smith wrote: > kangax wrote: >> I am trying to understand this IE peculiarity:
>> var x = 1;
>> delete x; // false (as expected)
> The global variable object is implemented as a JScript object, and the > global object is implemented by the host.
So the Variable object used during variable/function declarations *is not* the Global object in IE? That's against specs then (not surprisingly); in particular, 10.2.1 says:
"Variable instantiation is performed using the global object as the variable object and using property attributes { DontDelete }."
>> From what I understand, both - `window.x` and `x` should resolve to >> the same Reference, with base object being Global object and property >> name being "x". Both expressions - `delete x` and `delete window.x` - >> should evaluate to `false`, since `x` property of Global Object has >> {DontEnum} (due to that property created via variable declaration).
> DontEnum or DontDelete?
DontDelete of course. I had a feeling it wasn't a very good idea to write a post at ~2am :)
> DontDelete means that it is not enumerable on the variable object. What > makes - window.x - not deletable is that it is done in a variable > declaration.
> var x; // var makes a DontDelete binding > this.z = 1; // empty attributes
>> What I don't understand is why IE throws error in a latter case.
>> If `delete` is being passed a Reference with the same base object and >> same property name, why does it throw error in `delete window.x` case?
>> I suspect that the explanation is in identifier resolution process >> (10.1.4) and that base objects of References acquired as a result of >> evaluating `x` and `window.x` are not really the same objects.
> Yes, that is somewhat explained in the Eric Lippert blog entry.
> There is a bug in Firefox 2/Seamonkey where assiging to - window.x - > will wipe off the DontDelete attribute, making the - x - property > deletable.
That explains "weird" results I was getting in my tests. Tests were for attribute (DontEnum, DontDelete, ReadOnly) values of global properties (created via declaration, explicit property assignment and undeclared assignment).
> Apparently due to the bindings of inner and outer window objects, > assigning to a property of window removes the DontDelete. This is > something that came up in the GlobalScopePolluter discussion and I still > don't understand that well.
I remember. We should ask Mozilla folks.
> IE is obviously incorrect, too. Witness the freakshow of trying to get > the - name - property off the error that thrown by IE:-
> javascript: var isIn = 'foo' in self; var foo = 42; alert(isIn); try{ > delete self.p } catch(ex) { try { alert(ex.name); } catch(exex) { > alert("can't get error.name: " + exex.message); }}
> IE7: "can't get error.name: bad variable type"
> AIUI, enumerating over IE's global object, a host object, misses > enumerating over IE's global variable object.
So, presumably, variable declarations result in properties created on this "inaccessible" host Variable object. It's strange that something like this in a global scope - `var x = 1; Object.prototype.hasOwnProperty.call(window, 'x')` is `true`.
>> Looking at `delete` algorithm (specified in 11.4.1)
>> UnaryExpression: delete UnaryExpression
>> 1. Evaluate UnaryExpression. >> 2. If Type(Result(1)) is not Reference, return true. >> 3. Call GetBase(Result(1)). >> 4. Call GetPropertyName(Result(1)). >> 5. Call the [[Delete]] method on Result(3), providing Result(4) as >> the property name to delete. >> 6. Return Result(5).
>> Step 1 should evaluate UnaryExpression - `x` Identifier in this case. >> `x` is evaluated based on identifier resolution rules (10.1.4)
>> 1. Get the next object in the scope chain. If there isn't one, go to >> step 5. >> 2. Call the [[HasProperty]] method of Result(1), passing the >> Identifier as the property name. >> 3. If Result(2) is true, return a value of type Reference whose base >> object is Result(1) and whose property name is the Identifier. >> 4. Go to step 1. >> 5. Return a value of type Reference whose base object is null and >> whose property name is the Identifier.
>> When run in global scope, step 1 results in global object being found >> as next object in scope chain (unless there's another object injected >> before Global object (?)). Step 2 calls [[HasProperty]] of global >> object with "x" as a property name and evaluates to `true`. Step 3 >> returns Reference with 'base' object being Global object and property >> name being - "x".
> The global variable object is a different object in IE. There's no way > to get a reference to a variable object.
Ok. So it's this object that is found during identifier resolution in `delete x` as a "next object" in scope chain. That would explain why it looks like `x` and `window.x` resolve to Reference with different base objects.
>> 5. Call the [[Delete]] method on Result(3), providing Result(4) as >> the property name to delete.
>> - which calls [[Delete]] on Global with "x" as a property name; and -
>> 6. Return Result(5).
>> - returning result.
>> Step 5 should, of course, result in `false` value, since Global >> object's "x" property has {DontDelete} (due to "x" property being >> created via variable declaration), but that is irrelevant.
> It is relevant to the operation, but not related to the error thrown in IE.
> It doesn't matter what is attempted to be deleted, and error is thrown > always:-
> javascript: alert(delete window.undef)
> IE7: Error
>> Why does `window`'s [[Delete]] throw error when asked to delete an >> existent property (albeit with {DontEnum}) instead of returning `false`?
> You seem to be using DontEnum where DontDelete makes sense.
I meant DontDelete.
> var makes a DontDelete binding. Why calling delete on window throws in > IE has to do with IE's window being a host object. I can't explain more > than that.
>> Which object ends up being base of `x` Reference in `delete x;` and >> why does [[Delete]] on that object works as expected (contrary to >> behavior of base object of `window.x` Reference)?
>> And finally, why does -
>> window.window == window; // true, but >> delete window.window.x;
>> - result in a completely different - "Object doesn't support this >> action" - error?
> IE gets the window property which points to the Host object which can't > delete the variable - x -. Why? I don't know.
Ok.
> for(var v in window) - will fail to enumerate global properties in IE, > even though the property is "in" window.
From what I can see, IE 7 & 8 set DontEnum on properties created with var/function declaration *and* on those created via undeclared assignment. Explicit assignment leaves property enumerable:
And again, this hints at the fact that properties of host window object end up being DontDelete (i.e. `var x`, `function x(){}` and `x = 1`) whereas properties of `window` aren't (i.e. `window.x = ...`).
If this theory is correct, then it's not *property assignment* that matters, but property of *which object* that assignment happens on that matters.
> So, in IE, the variable VVVVVV is not going to show up in a for-in loop, > though the property assigned will. The - in - operator and > hasOwnProperty say "VVVVVV" is a property of self.
> In Opera 10, the variables VVVVV and XXXXX are not properties of - self > - and unsurprisingly, are not *own* properties of self.
> I cannot conclude much about IE's implementation of window and so I've > posted this message to m.p.s.jscript in hopes that someone there can > explain better.
> Garrett Smith wrote: > > kangax wrote: > >> I am trying to understand this IE peculiarity:
> >> var x = 1;
> >> delete x; // false (as expected)
> > The global variable object is implemented as a JScript object, and the > > global object is implemented by the host.
> So the Variable object used during variable/function declarations *is > not* the Global object in IE? That's against specs then (not > surprisingly); in particular, 10.2.1 says:
> "Variable instantiation is performed using the global object as the > variable object and using property attributes { DontDelete }."
> >> From what I understand, both - `window.x` and `x` should resolve to > >> the same Reference, with base object being Global object and property > >> name being "x". Both expressions - `delete x` and `delete window.x` - > >> should evaluate to `false`, since `x` property of Global Object has > >> {DontEnum} (due to that property created via variable declaration).
> > DontEnum or DontDelete?
> DontDelete of course. I had a feeling it wasn't a very good idea to > write a post at ~2am :)
> > DontDelete means that it is not enumerable on the variable object. What > > makes - window.x - not deletable is that it is done in a variable > > declaration.
> > var x; // var makes a DontDelete binding > > this.z = 1; // empty attributes
> >> What I don't understand is why IE throws error in a latter case.
> >> If `delete` is being passed a Reference with the same base object and > >> same property name, why does it throw error in `delete window.x` case?
> >> I suspect that the explanation is in identifier resolution process > >> (10.1.4) and that base objects of References acquired as a result of > >> evaluating `x` and `window.x` are not really the same objects.
> > Yes, that is somewhat explained in the Eric Lippert blog entry.
> > There is a bug in Firefox 2/Seamonkey where assiging to - window.x - > > will wipe off the DontDelete attribute, making the - x - property > > deletable.
> That explains "weird" results I was getting in my tests. Tests were for > attribute (DontEnum, DontDelete, ReadOnly) values of global properties > (created via declaration, explicit property assignment and undeclared > assignment).
> > javascript: var isIn = 'foo' in self; var foo = 42; foo = > > 43;alert([isIn, delete self.foo, self.foo]);
> > Apparently due to the bindings of inner and outer window objects, > > assigning to a property of window removes the DontDelete. This is > > something that came up in the GlobalScopePolluter discussion and I still > > don't understand that well.
> I remember. We should ask Mozilla folks.
> > IE is obviously incorrect, too. Witness the freakshow of trying to get > > the - name - property off the error that thrown by IE:-
> > javascript: var isIn = 'foo' in self; var foo = 42; alert(isIn); try{ > > delete self.p } catch(ex) { try { alert(ex.name); } catch(exex) { > > alert("can't get error.name: " + exex.message); }}
> > IE7: "can't get error.name: bad variable type"
> > AIUI, enumerating over IE's global object, a host object, misses > > enumerating over IE's global variable object.
> So, presumably, variable declarations result in properties created on > this "inaccessible" host Variable object. It's strange that something > like this in a global scope - `var x = 1; > Object.prototype.hasOwnProperty.call(window, 'x')` is `true`.
> >> Looking at `delete` algorithm (specified in 11.4.1)
> >> UnaryExpression: delete UnaryExpression
> >> 1. Evaluate UnaryExpression. > >> 2. If Type(Result(1)) is not Reference, return true. > >> 3. Call GetBase(Result(1)). > >> 4. Call GetPropertyName(Result(1)). > >> 5. Call the [[Delete]] method on Result(3), providing Result(4) as > >> the property name to delete. > >> 6. Return Result(5).
> >> Step 1 should evaluate UnaryExpression - `x` Identifier in this case. > >> `x` is evaluated based on identifier resolution rules (10.1.4)
> >> 1. Get the next object in the scope chain. If there isn't one, go to > >> step 5. > >> 2. Call the [[HasProperty]] method of Result(1), passing the > >> Identifier as the property name. > >> 3. If Result(2) is true, return a value of type Reference whose base > >> object is Result(1) and whose property name is the Identifier. > >> 4. Go to step 1. > >> 5. Return a value of type Reference whose base object is null and > >> whose property name is the Identifier.
> >> When run in global scope, step 1 results in global object being found > >> as next object in scope chain (unless there's another object injected > >> before Global object (?)). Step 2 calls [[HasProperty]] of global > >> object with "x" as a property name and evaluates to `true`. Step 3 > >> returns Reference with 'base' object being Global object and property > >> name being - "x".
> > The global variable object is a different object in IE. There's no way > > to get a reference to a variable object.
> Ok. So it's this object that is found during identifier resolution in > `delete x` as a "next object" in scope chain. That would explain why it > looks like `x` and `window.x` resolve to Reference with different base > objects.
> > [snip delete steps 1-4]
> >> 5. Call the [[Delete]] method on Result(3), providing Result(4) as > >> the property name to delete.
> >> - which calls [[Delete]] on Global with "x" as a property name; and -
> >> 6. Return Result(5).
> >> - returning result.
> >> Step 5 should, of course, result in `false` value, since Global > >> object's "x" property has {DontDelete} (due to "x" property being > >> created via variable declaration), but that is irrelevant.
> > It is relevant to the operation, but not related to the error thrown in IE.
> > It doesn't matter what is attempted to be deleted, and error is thrown > > always:-
> > javascript: alert(delete window.undef)
> > IE7: Error
> >> Why does `window`'s [[Delete]] throw error when asked to delete an > >> existent property (albeit with {DontEnum}) instead of returning `false`?
> > You seem to be using DontEnum where DontDelete makes sense.
> I meant DontDelete.
> > var makes a DontDelete binding. Why calling delete on window throws in > > IE has to do with IE's window being a host object. I can't explain more > > than that.
> >> Which object ends up being base of `x` Reference in `delete x;` and > >> why does [[Delete]] on that object works as expected (contrary to > >> behavior of base object of `window.x` Reference)?
> > for(var v in window) - will fail to enumerate global properties in IE, > > even though the property is "in" window.
> From what I can see, IE 7 & 8 set DontEnum on properties created with > var/function declaration *and* on those created via undeclared > assignment. Explicit assignment leaves property enumerable:
> And again, this hints at the fact that properties of host window object > end up being DontDelete (i.e. `var x`, `function x(){}` and `x = 1`) > whereas properties of `window` aren't (i.e. `window.x = ...`).
> If this theory is correct, then it's not *property assignment* that > matters, but property of *which object* that assignment happens on that > matters.
Yes. The window object has no standards. It may look a lot like the global object, but won't necessarily act like it. As long as you avoid treating it like it is the global object (or using undeclared globals), you won't have problems.