[[Delete]]'ing window properties in IE

358 views
Skip to first unread message

kangax

unread,
Sep 2, 2009, 1:24:46 AM9/2/09
to
I am trying to understand this IE peculiarity:

var x = 1;

delete x; // false (as expected)
x; // 1 (as expected)

delete window.x; // Error. Cannot delete 'window.x' (?)
x; // 1 (as expected)


// same when replacing `window` with `this`

delete this.x; // Error. Cannot delete 'this.x'
x; // 1 (as expected)


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?

--
kangax

Garrett Smith

unread,
Sep 2, 2009, 3:10:49 AM9/2/09
to
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.

http://blogs.msdn.com/ericlippert/archive/2005/05/04/414684.aspx

> 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]);

Seamonkey 1.1: true, false, 43
FF3.5: true, false, 43
IE 7: Error

javascript:var isIn = 'foo' in self; var foo = 42; window.foo =
43;alert([isIn, delete self.foo, self.foo + '']);
Seamonkey 1.1: true, true, undefined
FF3.5 true, false, 43
IE 7: Error

Seamonkey is incorrect.

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.

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
--
comp.lang.javascript FAQ: http://jibbering.com/faq/

Garrett Smith

unread,
Sep 2, 2009, 3:13:20 AM9/2/09
to
Garrett Smith wrote:
> kangax wrote:

> DontEnum or DontDelete?
>
> DontDelete means that it is not enumerable on the variable object. What

s/enumerable/deletable

Eric Bednarz

unread,
Sep 2, 2009, 7:06:29 AM9/2/09
to
kangax <kan...@gmail.com> writes:

> var x = 1;
>
> delete x; // false (as expected)
> x; // 1 (as expected)
>
> delete window.x; // Error. Cannot delete 'window.x' (?)

I suppose that’s with IE8 (JScript 5.8)?

With IE 6 & 7 I get the “Object doesn’t support this action” error
mentioned below.

> // same when replacing `window` with `this`

That’s rather unsurprising since ‘window’ and ‘this’ *are* strictly
equal in IE (unlike some other stuff, see below).

> And finally, why does -
>
> window.window == window; // true, but

but

window.window === window // false

and since

window === this // true

unsurprisingly

this.window === window // false

although

this === self // false
window === self // false

this.window === self.window // true

I must confess that I never understood any of this. ;-)

> delete window.window.x;
>
> - result in a completely different - "Object doesn't support this
> action" - error?

--
λ

kangax

unread,
Sep 4, 2009, 9:32:50 PM9/4/09
to
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 }."

>
> http://blogs.msdn.com/ericlippert/archive/2005/05/04/414684.aspx
>
>> 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]);
>
> Seamonkey 1.1: true, false, 43
> FF3.5: true, false, 43
> IE 7: Error
>
> javascript:var isIn = 'foo' in self; var foo = 42; window.foo =
> 43;alert([isIn, delete self.foo, self.foo + '']);
> Seamonkey 1.1: true, true, undefined
> FF3.5 true, false, 43
> IE 7: Error
>
> Seamonkey is incorrect.
>
> 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`.

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.

Ok.

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

DontEnum ReadOnly DontDelete

created_via_var_declaration true false true
created_via_func_declaration true false true
created_via_property_assignment false false Error
created_via_undeclared_assignment true false Error

(I'll attach a test page if needed)

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.

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

Thanks.

--
kangax

David Mark

unread,
Sep 5, 2009, 9:01:24 AM9/5/09
to

That's all the explanation there is.

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

Nobody knows. Ask the IE programmers if you are really interested.

>
>
>
> > 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:
>
>                                    DontEnum ReadOnly DontDelete
>
> created_via_var_declaration       true     false    true
> created_via_func_declaration      true     false    true
> created_via_property_assignment   false    false    Error
> created_via_undeclared_assignment true     false    Error

There you go. Stick with the first two.

>
> (I'll attach a test page if needed)
>
> 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.

[snip]

Reply all
Reply to author
Forward
0 new messages