Why does the delete keyword act opposite to expected? - javascript

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?

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.

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.

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

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

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.

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.

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.

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.

Related

Object Properties in Browser's Console

Whenever i log any javascript object into browser, I am keen to explore it by expanding what is inside in the console window,
As one such example is
console.log(console);
I sure found what is inside, But the real query starts now,
When i expand the object it has a property called __proto__ with its sub-properties inside, then again this has a property of contructor and the cycle goes on as it has __proto__ and again and so on.
Does it end ?
If Yes, what does this multiple repetition denotes ?
If No, Why doesn't browser hangs on printing such infinite object ?
Any Leads highly appreciated
Thanks & Regards
Shohil Sethia
If Yes, what does this multiple repetition denotes ?
Derek has already given you a link explaining prototype chain.
If No, Why doesn't browser hangs on printing such infinite object ?
__proto__ is a special property and will be handled in special way. Instead lets take a generic example:
var a = {
nest : function() {
this.b = this;
}
}
a.nest();
This will create an object a that has a property b that points to the main object a itself.
If you do console.log(a) you will see the similar behavior as you saw in case of __proto__. You can go on expanding property b N number of times and it will always show an object that has a property b and a method nest.
In this case browser doesn't hang because it iterates on only one level of properties. When you try to expand property b it will again iterate on only 1 level of sub-properties. It never iterates on nested properties and hence doesn't face any issue. On the other hand if you try to use JSON.stringify(a) it will give an error about circular reference because to generate string from the object it has to iterate on all nested properties.

In Javascript what is more efficient, deleting an element or setting it to undefined explicitly

Say I have an Associative Array Foo, with key bar and value xyz.
console.log(Foo['bar']);
>> xyz
delete Foo['bar'];
console.log Foo['bar'];
>> undefined
Foo['bar'] = 'xyz';
console.log(Foo['bar']);
>> xyz
Foo['bar'] = undefined;
console.log (Foo['bar']);
>> undefined
My question is, which of the two is more efficient, do they differ in any way? Is there a scenario when I should use one over the other?
Thanks!
Results:
Thank you to everyone for helping out and showing me jsperf. Setting it to undefined appears to be (relatively) significantly faster then delete, although all the caveats pointed out below are also very interesting (in fact, I will probably be using delete a lot going forward to avoid future errors out of leftfield).
Be aware that deleting a property from an object will replace that property with one of the same name if one exists on the prototype chain.
Setting the property to null or undefined will simply mask it.
I didn't benchmark the performance of those operations (as I mentioned in a comment, just create a little benchmark on http://www.jsperf.com), but I'll lose some words on the difference.
You will always be good on deleteing properties, wheras setting them to undefined or null will let people and/or code hang, which check with the IN operator.
Like
if( 'bar' in Foo ) { }
will still return true if you set Foo.bar to undefined. It won't if you go with delete Foo.bar.
It'll make a negative performance difference in the long term as b is still considered a property after the latter assignment to undefined. For example:
var a = { b : 0 };
a.b = undefined;
a.hasOwnProperty("b");
>>> true
Same goes for the in keyword ("b" in a is true) so this will most likely hinder iteration when part of a larger object.
After some experimentation and research, I have concluded that, at least on my machine, using the delete operator is 6 times slower than assigning undefined. Despite that, it is important to remember what jAndy said about the prototype chain and property existence.
If you'd like to re preform my tests, I instantiated an array of 50,000 items. (Item count doesn't matter as long as it is big enough to show a difference between both tests.)
let arr = new Array(500000).fill(null).map(() => ({"item": 42}));
And then I timed the amount of time it took to assign each object's item property to undefined in the array against the amount of time it took to delete each object's item property in the array.
console.time("Assignment To Undefined");
arr.forEach(object => object.item = undefined);
console.timeEnd("Assignment To Undefined");
//Reset the array here...
console.time("Deletion");
arr.forEach(object => delete object.item);
console.timeEnd("Deletion");
All of my results:
Deletion: Approximately 200 - 170 ms.
Assignment: Approximately 15 - 40 ms.
Apart for benchmarking the actual action of deleting or assigning to undefined, you should also consider the future performance implication of using the object after you changed it.
I can think of two:
Once the property is deleted, accessing it will cause JavaScript to look it up in the prototype chain, whereas if the property still exists on the object (with undefined value), the prototype chain won't be searched.
Deleting the property changes the "object shape" and might hurt JavaScript optimizations.
From the link I commented:
The delete operator removes a property entirely. Setting a property to
undefined removes the value. Setting a property to null changes the
value to the null value.
Technically they are not equivalent, but in practice they are often
used to mean the same thing: that a property is unset.
Setting the object to null was the fastest.
Some good resources to understanding these operations:
Understanding delete
Understanding JavaScript’s ‘undefined’
Since deleting the property doesn't do the same thing programmatically as setting it to undefined, it really depends upon what programming outcome you want.
delete Foo['bar']; removes the bar property from the Foo object. It will not be there if someone iterates over the direct properties of Foo.
Foo['bar'] = undefined sets the property to undefined, but it still exists on the object and will still be there, but have a value of undefined.
So, if you want to get rid of the property, use delete. If you want the property to still be there, but have an undefined value, then set it to undefined.
If you really just want to know which is fastest and for some reason don't care out the programming difference, then go to jsperf.com, make yourself two comparison test cases and run the jsperf in a bunch of different browsers that are relevant to you. All performance questions should be answered with relevant real world testing.

Extending Object's prototype has weird behavior in Chrome

Can anyone please explain why this code behaves so weird under Google Chrome:
<script>
console.log({someproperty:'hello'}) ;
Object.prototype.get = function(){} ;
</script>
The content of the object printed in the console has no "someproperty", instead it has a "get someproperty" which is a function.
I'm using Chrome 21.0.
Is this expected? Is it a bug?
I can't explain to you why setting Object.prototype.get to something different causes such odd behavior, except that that function is almost certainly what Chrome/Webkit is using behind the scenes to generate its fancy object logging.
I can tell you that the reason it's happening even though you're setting .get after the console.log is that Chrome/Webkit doesn't retrieve the object until you actually click the arrow in the console to expand the object. You can test this by running this jsfiddle: http://jsfiddle.net/BNjWZ/
Notice that if you click the arrow to expand the object right away, the object will look normal, but if you wait three seconds for the .get = function(){}; to be called, it will have the 'get's.
I'm seeing this behavior (both the odd 'get' in the object display and the delayed object logging) in 22.0.1229.79
It is not expected.
There is no restriction in the spec as to the name of a property. So 'get' is a legal name for a property of an object, and the prototype object, for that matter.
It seems to be a bug in the global dir() function of the console.
Addition: JQuery has a problem with 'get' and 'set' properties.

What is the purpose of the delete operator in Javascript?

The behaviour of the delete operator seems very complicated and there are many misunderstandings about what it actually does. To me, it seems that reassigning something to undefined will more reliably do what you would expect.
I've never seen the delete keyword in Javascript actually used in non-example code and I am wondering if it is particularly useful for anything. Does delete have any purpose that cannot be acheived by reassignment to undefined? Is it used at all in any of the famous libraries (e.g. jQuery, dojo, backbone, etc)?
Does delete have any purpose that cannot be acheived by reassignment to undefined?
Yes. If you want to unmask a property from a prototype or cause in, hasOwnProperty, and for (...in...) to not record the property as existing then delete is appropriate.
let set = {};
set._x = true;
alert('_x' in set); // true
set._x = undefined;
alert('_x' in set); // true
delete set._x;
alert('_x' in set); // false
EDIT: As T.J. Crowder explains:
The purpose of the delete operator is to completely remove a property from an object, whereas setting a property to undefined just sets the property to undefined.
This matters in its own right, but it also matters when you're using inheritance, because if O derives from P
let P = { prop: 42 };
let O = Object.create(P); // P is O's prototype.
when you retrieve O.prop, you get the value of prop from O if O has a property with that name (even if its value is undefined), but if O doesn't have the property at all, then the value will be retrieved from P.prop instead.
console.log(O.prop); // "42" since O doesn't have its own prop, but P does.
O.prop = undefined;
console.log(O.prop); // "undefined" since O has its own prop.
delete O.prop;
console.log(O.prop); // "42" since the delete "unmasked" P.prop.
As Mike Samuel points out in his answer, one of the most common usages of delete is when you are treating an object as a "property bag" that associates names with values. There is logically a difference between "this name is now mapped to some bogus value" and "this name is not mapped at all". "delete" achieves the latter.
That's all reasonably well understood. I thought I might add an interesting historical note regarding the JScript 1.0 through 5.0 engines.
In those original Microsoft implementations of JScript we used OLE Automation-style IDispatch objects to implement expando objects. IDispatch of course works by associating a name with a "dispatch id", which is simply an integer. To invoke dynamically, first you ask the dispatch object to give you the dispatch ID associated with a name, and then you say "now invoke the method associated with this ID, given these arguments".
That's all well and good. But one of the requirements of the IDispatch contract is that the mapping from name to dispatch ID be stable over the entire lifetime of the object. So if someone says "add property Foo to this object", then we might decide that property Foo is associated with dispatch identifier 0x1234 in that object. From that moment on, every time the object is asked for the dispatch identifier of "Foo", it must give back 0x1234, even if Foo is deleted and subsequently added again. This permits a caller to maintain their own fast cache of name/dispid pairs rather than always having to ask the object on every invocation.
The practical upshot of that is that "delete" does not in any way lessen the memory burden on the object in that implementation! When you delete a property (in the original implementation) we must add a bit to the object marking that dispatch identifier as deleted, but we must retain all the information about the name/id pairing in case that name ever comes back. Adding a huge number of properties to an object and then deleting all of them does not shrink the object in memory.
The JScript engine has of course been completely rewritten since my time (except for, I believe, the parser and lexer) so I have no idea if the engine still has this unusual quirk. It would be interesting to find out.
If you do
delete Foo.Bar;
it deletes the property Bar from object Foo entirely
Foo.Bar = undefined
merely sets Bar property to undefined and Foo.Bar still exists
The other answers are explaining the motivation behind the delete keyword. I would like to add that as of 2017, browser do deallocate memory both when deleting a property and when setting the property to undefined.
Consider this example (source of roughSizeOfObject()):
> var obj = {a:42,b:"b"}; roughSizeOfObject(obj)
26
> obj.a = undefined; roughSizeOfObject(obj)
18
> delete obj.a; roughSizeOfObject(obj)
10
> obj.b = undefined; roughSizeOfObject(obj)
8
> delete obj.b; roughSizeOfObject(obj)
0
The example comes from Chrome 61 (64-bit) console (note that all characters in String are internally encoded as 16-bit unsigned integer).
You can check the answer of the following link Can I set variables to undefined or pass undefined as an argument? which explains the difference in a very detailed way.
Summary:
You can certainly assign undefined to it, but that won't delete the
variable. Only the delete object.property operator really removes
things.
delete is really meant for properties rather than variables as such.
Browsers will let you get away with straight delete variable, but it's
not a good idea and won't work in ECMAScript Fifth Edition's strict
mode. If you want to free up a reference to something so it can be
garbage-collected, it would be more usual to say variable= null.
Well, you'd end up with an element in your object that contains the value undefined. The key wouldn't be gone.

Why is comparing the ".constructor" property of two windows unreliable?

I'm trying to understand why the following is not working as I would think it does. I'd like to check whether an object is a window. I was thinking that checking the constructors of the current window and another window would work.
So, first creating another window:
var popup = window.open('', '', '');
And then checking with:
popup.constructor === window.constructor;
But for some reason the results vary among browsers:
IE7: Returns true
But that's simply because .constructor === undefined
IE8: Returns false
IE9: Throws an error with no text ("")
Chrome: Returns false
Though in both cases .constructor === DOMWindow - but DOMWindow is not accessible directly like this
Opera: Returns false
Though in both cases .constructor === Object
Firefox: Returns false
Though in both cases .constructor === Window
Why isn't this reliable and working correctly? jQuery simply checks for "setInterval" in window, but I'd like to create a more robust function for checking whether an object is a window.
Every separate window is a separate global context for the interpreter (and yes I'm ignoring IE7 for the moment). Your issue really becomes clear when you think about instanceof:
if (something instanceof Array) { ... }
The problems with that occur when "something" is a value that was constructed in a separate context, like an iframe for example. Each window has it's very own copy of the "Array" constructor, so even though an object may be an Array instance, it's not an instance of the same constructor function.
I suspect that the IE7 behavior is related to some other weirdness that I think is something like window.window being not equal to window. Like, one of them is in fact a reference to something internal. (Maybe it's window not being equal to window.self even though they otherwise behave identically. I don't invest a lot of philosophical thought time in contemplating IE7.)

Categories

Resources