10

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?

Randomblue
  • 112,777
  • 145
  • 353
  • 547

8 Answers8

12

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.

Oleg V. Volkov
  • 21,719
  • 4
  • 44
  • 68
  • 1
    Sorry, but `window` does have a prototype: `this instanceof Window` === true, where `this === window`. and `Window.prototype.alert = function(a){console.log(a);}` silences the alerts, and just logs them, check my answer for some more checks on Window.prototype (it inherits from the mother of all objects: `Object.prototype`) – Elias Van Ootegem Sep 11 '12 at 10:37
  • @EliasVanOotegem, hm, looks like so - I've only checked magical `__prototype__`. Also, Firefox, discussed in other answers seems to have native implementation for it. Still, it doesn't change main point about unshadowing. I'll remove note on prototype for now. – Oleg V. Volkov Sep 11 '12 at 10:55
  • the global object's prototype is hidden, but it's there. When checking `window.prototype`, it returns `undefined`, that's why use of `Object.getPrototypeOf` is preferred over `prototype` or magic `__proto__` and others. And yes, your point is a valid one. As far as I can tell, all answers here say the same thing, really – Elias Van Ootegem Sep 11 '12 at 11:24
  • If you actually check the prototype chain, you'll see that this is not what is happening. You can also check Object.getOwnPropertyDescriptor(window, 'console') to confirm that 'delete window.console' really does reinstate an *own* property. – ndkrempel Feb 03 '14 at 13:31
  • @ndkrempel, did you miss last paragraph by any chance? – Oleg V. Volkov Feb 03 '14 at 17:23
  • If you deleted your first three paragraphs, the last paragraph might be acceptable. As it stands, you're conflating JavaScript's prototype chain with implementation details. – ndkrempel Feb 04 '14 at 13:06
3

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.

Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149
  • Do you think I can modify the prototype of the window object? – Randomblue Sep 10 '12 at 12:06
  • Not as such: window.status = 'Foobar' is modifying the property, but `status = []; console.log(status);` logs "''", so you can't change the type. Most of the global object is read-only. I've just edited my answer a bit, but as I said there's a lot more to it than just the prototypes: JS does scope-scanning first: variables in current & parent scope are matched first, then properties of the current global object, then the `Window` prototype, then the `Object` prototype (that's the long and short of it). – Elias Van Ootegem Sep 10 '12 at 12:12
  • I've tried a few things, as it turns out: some things _can_ be changed, for example: `Window.prototype.alert = function (a){console.log(a);}` followed by `alert('Foo');` doesn't alert, but logs `Foo`. But just like `Object.prototype`, I'd say: augment, if you insist but know that messing with these prototypes can cause you major headaches – Elias Van Ootegem Sep 10 '12 at 13:16
  • @EliasVanOotegem, what do you make of `window.hasOwnProperty('console') //=> true` and `window.__proto__.hasOwnProperty('console') //=> false` ? With your Explanation, it should be exactly the other way around. Also, when you set `window.console = 2`, the console is virtually gone. The prototype cannot resurrect it: `window.__proto__.console //=> undefined` – Beat Richartz Sep 11 '12 at 15:14
  • It presents itself as an instance property, true, but it's important to note that the global object is persistent, and global properties are [not enumerable](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/propertyIsEnumerable) `window.propertyIsEnumerable('console')//=> false` even after you redefine it. Compare that to `var bar = 'foo';` which will be enumerable. I had hoped to back my claims up with `hasOwnProperty`, but the global object is hard to expose at times, but this is as close to proof I get, that the console is derived from the prototype chain – Elias Van Ootegem Sep 11 '12 at 15:27
  • `window.console = 2; window.propertyIsEnumerable('console') //=> true` returns true for me. – Beat Richartz Sep 11 '12 at 15:33
  • @BeatRichartz: I've found _proof_ of my explanation: the console object is a property of the global object. – Elias Van Ootegem Sep 11 '12 at 15:50
  • @EliasVanOotegem I'm not sure I understand what you mean by «global object». In browser javascript implementations, `this` in the development console references `window`, and window is the global object: `this == window //=> true`. This is also why you can type just `console`, and `window.console` appears, because `console` calls `this.console` which equals `window.console`. Also, `window.parent` or therefore `this.parent` is a circular reference to window: `window.parent == window //=> true`. `console` also appears as a property on `window` itself, and by seeing this, you only *proove*... – Beat Richartz Sep 11 '12 at 16:15
  • 1
    ...that `console` is not inherited from the prototype. The prototype of `window`, `window.__proto__`, does not have a `console` property. Therefore it is surely not prototypical inheritance, as you say. Also, `Window`, the constructor of window, is *not* a standard constructor, which you can see by typing `new Window()` which fails with `Illegal constructor`, and a `Console` constructor, as in your answer, is not defined in the global namespace. The only thing proving your argument would be that you can somehow use the `console` after reassigning it `console = 2` without using `delete`. – Beat Richartz Sep 11 '12 at 16:21
  • And, another thing is: `this.constructor` does not return the [prototype](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/prototype) as you say in your answer, it returns the [constructor](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/constructor) – Beat Richartz Sep 11 '12 at 16:31
  • @BeatRichartz: the global object has no name, window is one of the properties of the global object, that forms a circular reference, (check: `this.window === window`). I know about `this` <> `window` and all that. All I said was that the global object you have access to is not the same as the "full version", for that you need window.parent, or this.parent. Sure, it's evaluates to true when compared to `window`, but look in your console, there's a lot more properties that show up, including the console. I also never claimed that `this.constructor` returns the prototype, – Elias Van Ootegem Sep 11 '12 at 18:27
  • But I did say that `this.constructor.name` shows `Window` as the constructor name, and that `Window.prototype` does exist. That was in response to Oleg's answer because he stated that the global object does not have a prototype. Of course it's an illegal constructor, just like `Console` is illegal: there is only 1 global object, and only 1 console. It stands to reason that instantiating them within the same global scope is a bit of a no-no. Bottom line: as you say so yourself: I prove that the console obj still exists, even after reassigning, which IMO answers the question at hand here – Elias Van Ootegem Sep 11 '12 at 18:31
  • but `console` does not belong to `window.__proto__` or `window.parent.__proto__`. I can't understand from your answer how does the original Console object "survive" after we set `window.console=0` and then `delete window.console` ?? – Hrant Khachatrian Sep 11 '12 at 21:52
  • @HrantKhachatrian That's exactly the issue with this answer. – Beat Richartz Sep 11 '12 at 22:03
  • It's perfectly simple: the global object is persistant, and none of its properties can be removed ([MDN](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/propertyIsEnumerable)). By setting `window.console = 1` you're actually sitting a variable in the global scope, not an object property. Since JS does scope searching prior to scanning the global object chain, that variable will be found when using `console`, once that var is deleted, the console object (which was never gone) is accessible again – Elias Van Ootegem Sep 11 '12 at 22:13
  • try `bar = console; console = 1; bar.log('foo');` The console object is still there, it was _never_ altered, the merely masked by a variable that had some other value assigned to it – Elias Van Ootegem Sep 11 '12 at 22:14
  • @EliasVanOotegem The global object in browser javascript *is* `window`: `this === window //=> true`. Same with window.parent, it's a circle reference. You can try all you want: `window.parent === this //=> true`. Your claim that window.parent has more properties than window is not true: `var value = true; for (prop in window.parent) { value = (window.parent.hasOwnProperty(prop) == window.hasOwnProperty(prop)) } //=> value remains true`. Also, `Console` as a constructor is not illegal, it's just not there. Overwriting a variable is *not* affecting prototypical inheritance at all. – Beat Richartz Sep 11 '12 at 22:30
  • `window.console` and therefore `console` are in essence pointers to the instantiated console. With the little difference that they cannot be deleted, but as the window object behaves like a normal javascript object, overwriting the variable works. So with `console = 2` you actually set an object property on the global/window object, which is exactly why it masks the native pointer. What you do by `bar = console; console = 1; bar.log('foo');` is assigning the instantiated Console to another variable, but it does not answer why delete `window.console` resurrects the console. – Beat Richartz Sep 11 '12 at 22:39
  • 1
    To wrap it up: in `nodejs` (to deliver an example of non-browser javascript handling the same issue), `delete this.console` actually deletes the reference to console. But `this.console = 2` does not reassign console. So `console = 2;` and resurrecting the console by `delete console` *has nothing to do* with prototypical inheritance. It's just the browsers implementation of the `window` that does the magic behind all this. – Beat Richartz Sep 11 '12 at 22:46
  • @BeatRichartz: the gloabl object _is_ window, because window is a property of the global object that points to itself. The global object _has no name_. I never said that `parent` had more properties, but look in your console and you'll notice that when typing this or window vs window.parent, the total number of properties listed is greater: less of the global object is hidden. I know what I did with `bar = console`, no need to explain. My point there was: the console can still be referenced, so console = 1 doesn't delete the console object, unlike what the OP thought, he talks about recreation – Elias Van Ootegem Sep 12 '12 at 06:39
  • There really is no need for all this banter. bottom line is: 99% of times you say exactly what I'm trying to say. The only point on which we seem to disagree, really, is that `window` is or is not the direct access point for the global object. I say it isn't based on what Douglas Crockford says on the global object. You say it is because that's what it seems to be. which ever way you cut it, the main thing is: assigning to console is masking the console object, that's why it's _"hidden"_, not deleted. I think we agree on that – Elias Van Ootegem Sep 12 '12 at 06:43
  • @EliasVanOotegem Well, what you say is right, the global object **is** window in browser javascript. Stress on the *is*. And Douglas Crockford *isn't* always 100% exact or right, just to say. "In browsers, the global object always contains a window member whose value is the global object" is right, that's why the apparently senseless `window.window.window` etc exists, but he missed the fact that `this` *is the same object as* `window`. You also said it has to do with prototypical inheritance, it has not. I fail to see more properties on `window.parent` than on `window` in my console... – Beat Richartz Sep 12 '12 at 07:32
  • ...and if you do, you should think about updating or switching browsers. – Beat Richartz Sep 12 '12 at 07:33
  • He doesn't mention the fact that `this` is the global object, in the global scope, but that sort of goes without saying (if you're not in strict mode). I know about `window` and `this` etc... nothing new there. I see more properties on `window.parent` in the latest version of google chrome. In FF (latest), I see the console show up in window, too. You're right, there's no prototype-stuff here (I'll edit for that) but console is a property of the global object, and (in browsers at least) therefore it can't be deleted – Elias Van Ootegem Sep 12 '12 at 11:13
  • Strict mode does not meddle with `this` being the global object, [it prevents accidental globals](http://www.nczonline.net/blog/2012/03/13/its-time-to-start-using-javascript-strict-mode/). All the console reassignments and code works in strict mode. Please name one property you see on `window.parent` and not on `window` on latest chrome. Properties of the global object can be deleted, *native* properties can not. The oddity with console is, that `delete this.console` returns true (successful delete, attempting to delete readonly attributes returns false) but does not delete the console. – Beat Richartz Sep 12 '12 at 11:54
  • Please, let it go. Strict mode _does_ change `this`: `(function(){'use strict'; console.log(this);})()` logs undefined. In order not te create accidental globals when calling a constructor without the `new` keyword. Name one property: `console`, although now it shows up in window again... must have messed up the `Window.prototype` too much. Ok, I'll give you that. The delete rule applies for _all_ properties (`delete window.onclick; delete window.parent;` all return true). That's just one of the quirks, get over it. – Elias Van Ootegem Sep 12 '12 at 12:02
  • You're talking about [strict mode for functions and not script-wide strict mode](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/Strict_mode). [Here's a example of script-wide strict mode, `this` is still the global object, and global variable assignment fails](http://jsfiddle.net/kNkQy/). [Here's the same without strict mode](http://jsfiddle.net/PJPfp/). The `delete` returning true but not deleting applies for *native* properties: `this._prop = 2; delete this._prop; this._prop //=> undefined`. I will make no further comments, read up. – Beat Richartz Sep 12 '12 at 12:44
2

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

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
Peeter
  • 9,282
  • 5
  • 36
  • 53
  • You still get true in Chrome's Javascript console. `"use strict"; delete console` --> true – Jeremy J Starcher Sep 01 '12 at 00:43
  • 1
    I see no explanation for the "rebirth" of `console`. – Randomblue Sep 01 '12 at 00:46
  • I'm guessing its re-evaluated on delete to verify that it indeed cannot be deleted. – Peeter Sep 01 '12 at 00:49
  • @JeremyJStarcher: console uses eval, so you'll have to wrap it up in a function to check strict-mode in your console: `(function(){"use strict"; delete console;})()` – Elias Van Ootegem Sep 10 '12 at 09:13
  • 1
    This answer is not the right explanation. In strict mode there is an essentially syntactic restriction that you cannot do "delete " - which previously would have had the meaning of "delete .". But you can still do "delete window.console" in strict mode, and that generates no error and returns true. (You can check with Object.getOwnPropertyDescriptor(window, 'console') that the property is configurable.) – ndkrempel Feb 03 '14 at 13:28
2

First, this is not just the console, you can do this with every native property every browser-defined property on window.

setTimeout = 0;
setTimeout //=> 0
delete window.setTimeout;
setTimeout //=> function setTimeout() { [native code] }

Properties that are part of the ECMA-Script Spec can be fully overwritten & deleted:

Array = 0;
Array //=> 0
delete window.Array;
Array //=> ReferenceError

You can nearly overwrite any property on window, delete the overwrite and get back to the normal function.

The simple reason for this is that console and all the other native global functions browser defined properties are not linked to the DOMWindow Object via javascript but via C++. You can see the console being attached to the DOMWindow right here and the implementation of DOMWindow here

That also means that the window object is somehow a C++ Object masked as a javascript object the window object is at least partly defined by C++, and it is not prototypical inheritance doing the magic: Take for example:

window.hasOwnProperty('console') //=> true, console is defined directly on the window
window.__proto__.hasOwnProperty('console') // => false, the window prototype does not have a console property

Also, if it was prototypical inheritance, the following would lead to console returning 3:

window.__proto__.console = 3;
delete console;
console //=> still returns console;
window.hasOwnProperty('console') //=> the window still has it.

The same with a property respecting prototypical inheritance:

window.someProp = 4;
window.__proto__.someProp = 6;
someProp //=> 4
delete someProp;
someProp //=> 6

Therefore, when you set console to anything, it is gone and can only be resurrected by (hoorray for the irony): delete console.

So, what it means is that you cannot delete any native properties on the window object. Try to delete window.console when it is not overwritten, it will just pop up again. The fact that you are able to overwrite it in the first place (even in strict mode) without receiving any kind of warning (in my eyes) one of the key vulnerabilities of javascript (set setTimeout on nearly any page to 0 and see it tear itself apart), but as they say in spiderman:

With great power comes great responsibility

Update

To include a hint that this is specific to the implementation of the browser / engine and not any requirement of the language itself: In nodejs, deleting both engine-specified properties and ecma-script properties on the global object works:

delete this.console //=> true
console //=> ReferenceError

delete parseInt //=> true
parseInt //=> ReferenceError
Beat Richartz
  • 9,474
  • 1
  • 33
  • 50
  • this is true regarding all the objects that come from browser, not the language itself! for example one can try `delete parseInt` or `delete Array` and they'll be removed. – Hrant Khachatrian Sep 12 '12 at 18:29
0

The exact same thing happens in Firefox.

I'm assuming the following, based on observations of my own.

Variables are first checked to see if they match local variables, if not, then it will be checked to see if they match window.variable.

When you set console to 1, you set the local variable console to 1, so any lookups will see that instead of window.console (which still exists). When you delete console the local variable console gets deleted. Now any lookups of console will match window.console. That's why you get the behaviour you get.

I am assuming this based on experimenting with the JavaScript interpreter in Firefox. And, I'm sorry about incorrect terminology (feel free to edit), I'm not that experienced with namespaces.

Kevin Johnson
  • 913
  • 7
  • 24
  • You can check, for example with Object.getOwnPropertyDescriptor(window, 'console'), that you really are changing the global console, not a local variable. – ndkrempel Feb 03 '14 at 13:34
0

The delete operator removes a property from an object.

...

You can use the delete operator to delete variables declared implicitly but not those declared with the var or the function statement.

See delete on MDN

Edit:

See also Understanding delete if you're really into hardcore JavaScript.

Alexei
  • 672
  • 1
  • 5
  • 13
-1

What happens is you are overwriting the objects prototype, then you delete the overwritten value and what is left... is the original object, which is it's prototype.

Gung Foo
  • 13,392
  • 5
  • 31
  • 39
-2

Expected behavior. Little known fact that the Javascript console does not run in the browser's global space, but rather it runs within its own anonymous function.

I know that different browsers handle things differently, but in short -- delete does not operate as expected because it isn't operating in the global space.

If you really want to see things break, try playing with delete window.console

Ok, it is official -- I'm an idiot. One of the new features in ECMAScript is the ability to declare a property as dontdelete. Sorry about that confusion.

Jeremy J Starcher
  • 23,369
  • 6
  • 54
  • 74
  • Maybe I'm doing it wrong, but trying `delete window.console` still results in console being an object. – Kautiontape Sep 01 '12 at 00:34
  • Could you please point to code relating to this "anonymous function"? – Randomblue Sep 01 '12 at 00:35
  • @Kaution -- Yea, I'm seeing that. Been a while since I've tried randomly deleting things. – Jeremy J Starcher Sep 01 '12 at 00:35
  • @Randomblue -- Just do a search for "Javascript" and "anonymous functions." You'll find tons of material out there. You can get started here, though. http://stackoverflow.com/questions/1140089/how-does-an-anonymous-function-in-javascript-work – Jeremy J Starcher Sep 01 '12 at 00:38
  • I know what an anonymous function is in JavaScript. I'm interested in the C++ implementation inside Chrome. – Randomblue Sep 01 '12 at 00:41
  • @Randomblue:[here's where you can find everything and more](http://code.google.com/p/v8/), including the C++ code. If you're really interested, just checkout the code – Elias Van Ootegem Sep 10 '12 at 23:51
  • Occurs in console and in actual scripts. Also not related to DontDelete (or Configurable as it's now called), since the console property *is* configurable. – ndkrempel Feb 04 '14 at 13:19