Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the delete keyword act opposite to expected?

In Chrome, try the following in the console. First

console = 0;

to assign the value 0 to console. Then

console // (prints `0`)

to check we have correctly overwritten console. Finally,

delete console

Surprisingly, console now holds the original Console object. In effect, the delete keyword "resurected" console, instead of exterminating it!

Is this expected behaviour? Where is this implemented in the Chromium code?

like image 750
Randomblue Avatar asked Sep 01 '12 00:09

Randomblue


3 Answers

As mentioned in MDN's documentation on delete:

If the delete operator succeeds, it removes the property from the object entirely, although this might reveal a similarly named property on a prototype of the object.

Your delete simply unshadows native property inherited through prototype chain.

Some browsers have window inherit from native prototype and you'll have check out sources to see how property is inherited, if you really want to know that much details, but mostly they work just like JS' own.

like image 103
Oleg V. Volkov Avatar answered Sep 24 '22 14:09

Oleg V. Volkov


Got it:

I've managed to prove the console is a property of the global object: just open your console and type: this.parent or window.parent. This will show a more complete list of properties and methods at your disposal. Including console: Console, about 2/3 of the way down, just below chrome: Object (interesting...:)). I thought of this when I remembered that I somehow managed to change the CSS rules of the console itself (in chrome, don't ask me how I got there, I can't remember).
Bottom line: console ís a property of the window object. I think this backs up my explanation rather well.


@Randomblue: Since you're interested in how this is implemented in v8 you can check the trunk here, or browse the bleeding. Somewhere you'll find a test dir, that has a number of files that deal with delete. Special attention is given to delete used on global variables/properties: they can't be deleted, in other words: the console is never really gone. I would like to know why this answer went from being voted helpful and accepted to not-helpful and not-accepted, though...


It's perfectly simple. Console isn't some random, stand-alone, object. It's actually a property of the global object. Open your console and type this.console === console or window.console === console. It logs true, of course.

So thanks to implied globals console = 0 is pretty much the same as window.console = 0. You're sort of reassigning a property of an instance. The difference with normal objects is that the global object isn't just any old object: it's properties cannot be deleted (somewhere here on MDN). So your global is masking the console object, which is still there, you've just lost your reference too it:

var bar = window.console;
console = 12;
bar.log(console);//logs 12, bar is now an alternative reference to the console object
delete console;//unmasks the console reference
console === bar;//true

Don't, for a moment, be fooled into thinking the global object doesn't have a prototype. Just type this.constructor.name and lo and behold: Window with a capital W does appear. Another way of double checking is: Object.getPrototypeOf(this); or Object.getPrototypeOf(window);. In other words, there are prototypes to consider. Like always, the chain ends with Object.prototype:

 Object.getPrototypeOf(Object.getPrototypeOf(window));

In short, there is nothing weird going on here, but the weird nature of the global object itself. It behaves as if there is some form of prototypal inheritance going on. Look at the global object as though it were set up like this:

this.prototype.window = this;//<-- window is a circular reference, global obj has no name
this.prototype.console = new Console();//this is the global object
this.hasOwnProperty(console);//false
console = 0;//implied global

When attempting to access console, JS finds the property console you've just set prior to the instance of the Console object, and happily returns its value. The same happens when we delete it, the first occurance of console is deleted, but the property higher up the prototype chain remains unchanged. The next time console is requested, JS will scan the inheritance chain and return the console instance of old. The console-object was never really gone, it was merely hidden behind a property you set yourself.

Off topic, but for completeness' sake:
There are a few more things too it than this (scope scanning prior to object/prototype chain searching), due to the special character of the global object, but this is, AFAIK, the essence of it.
What you need to know is that there is no such thing (in JS) as an object without (at least) 1 prototype. That includes the global object. What you're doing merely augments the current global object's instance, delete a property and the prototype takes over again. Simple as that. That's what @Peeter hinted at with his answer: implied globals are not allowed in strict mode, because they modify the global object. Which, as I tried to explain here, is exactly what happens here.

like image 28
Elias Van Ootegem Avatar answered Sep 22 '22 14:09

Elias Van Ootegem


Some properties of the window object aren't deletable. True is returned because you aren't running in strict mode. Try the following (not in console):

"use strict";
delete console;

and you will get an exception (JSFiddle).

You can read more about how this is handled at http://es5.github.com/#x11.4.1

like image 23
Peeter Avatar answered Sep 22 '22 14:09

Peeter