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.