A puzzle to start things off – what is going on here?
var a = "cat";
(function() {
var a = "dog";
delete a;
a = "fish";
console.log(a);
})();
console.log(a);
The intention is to remove the local version of a from scope so that we can reassign the global version of a to “fish”. Will it work? What will be printed out?
- a) fish, fish (success)
- b) fish, cat (failure)
- c) a different combination
- d) depends on the browser (always a good option if you’re guessing)
- e) depends on the execution context
The answer in this specific case is b, the answer to more general questions about this sort of thing is e (we’ll come back to that later). It doesn’t work! The value of the global version of a is unchanged. This demonstrates a fundamental point about the delete operator:You cannot delete anything declared with var.
This applies even to variables that you declare within functions; once you declare them they exist forever. Bear this in mind if you want to remove something from scope using the delete operator, because it won’t work. We can fix the code by explicitly reassigning the global version of a rather than using delete:
var a = "cat";
(function() {
var a = "dog";
this.a = "fish";
console.log(a); // prints "dog"
})();
console.log(a); // prints "fish"
So, what is going on with the delete operator?
First things first
The delete operator returns a boolean value. We can use this to get a better understanding of how it works (and doesn’t work). However there is one gotcha with the return value – whether it is true or false depends on whether the object exists afterwards, not whether the delete was successful.
It’s a subtle difference, but it means that delete will return true if you try to delete something that never existed in the first place. You might not expect that. I didn’t.
var a = "you can't delete me!";
console.log(delete a); // prints false c
onsole.log(delete octopus); // prints true!
Deleting in global scope
Everything you declare in global scope, with or without the var keyword (which creates the new variable in local scope), also becomes a property of the global object.
var a = "apples";
b = "oranges";
console.log(window.a); // prints "apples"
console.log(window.b); // prints "oranges"
It’s worth mentioning at this point that declaring a variable without the var keyword is illegal in ECMAScript and will throw an error if you try to do it in strict mode. This is a good thing because declaring a variable without using var often indicates that the programmer mistyped the name of an existing variable. Strict mode makes this kind of error fail fast. Unfortunately strict mode is not widely used, so for now we will have to pretend it doesn’t exist.
Going back to the example, let’s see what happens if we try to delete a and b.
var a = "apples";
b = "oranges";
console.log(delete a); // prints false
console.log(delete b); // prints true
console.log(window.a); // prints "apples"
console.log(window.b); // prints undefined
Since it was declared with var the variable a is an object in global space and can never be deleted. However variable b was declared using sneaky shorthand to create a property on the global object, and properties can be deleted. In fact this is the main purpose of the delete operator: The delete operator is for deleting properties, not objects.
So far, so good!
Deleting in function scope
The good news is that delete in function scope works the same way as in global scope. However it does provide a good demonstration of the point above, which is that failing to use the var keyword causes things to leak into global scope.
(function() {
var a = "apples";
b = "oranges";
console.log(window.a); // prints undefined
console.log(window.b); // prints "oranges"
console.log(delete a); // prints false
console.log(delete b); // prints true
console.log(window.a); // prints undefined
console.log(window.b); // prints undefined console.log(a); // prints "apples"
console.log(b); // Error: b is not defined
})();
The example shows that the local variable a which was correctly initialised with var can never be deleted, but b (which is actually window.b) can be deleted.
Deleting in eval scope
Back at the start of this article I claimed that the answer to the initial puzzle depends on the “execution context”. JavaScript has three execution contexts, and the good news is that we have already covered two of them:
- Global code
- Function code
- Eval code
The bad news is that the third case causes the delete operator to behave differently to what we have seen so far. The worse news is that it causes us to throw out what has so far been a golden rule – you can’t delete anything created with the var keyword.
eval('var a = "cat";' +
'console.log(window.a);' + // prints "cat"
'console.log(delete a);' + // prints true (?!)
'console.log(window.a);' + // prints undefined
'console.log(a);'); // Error: a is not defined
In this case we declare var a and see that a has found its way on to the window object, which is familiar behaviour implying that we are in global scope. However things then take a twist when we discover that inside an eval statement it is possible to delete a.
You might be thinking “who cares, I never use eval”, but just remember that browser developer tools such as the Firefox Scratchpad work by putting whatever code you enter into an eval statement. That’s why you might not have come across some of the behaviour in this article before, and why you can’t test it using browser tools. You need to actually create an HTML page with a script if you want to see how delete really works.
A few other things you can’t delete
Some objects and properties are specifically flagged with a property indicating that they cannot be deleted, such as the length property of arrays and functions or the window object.
console.log(delete window); // prints false
console.log(delete undefined); // prints false
console.log(delete [].length); // prints false
console.log(delete function() {}.length); // prints false
These restrictions are designed to stop you from getting into a mess. This is a pleasant surprise coming from a language that allows you to redefine undefined.
The precise way that these things become undeleteable relates to a feature of JavaScript called “property attributes”, which may be the subject of a future blog in this series.
Final points
We now know that you can’t delete anything declared with var unless you are in eval execution. Given that we also know that declaring variables without var is illegal, and we also know from various blog posts that using eval is generally a bad idea, we can come to the slightly surprising conclusion:You can never delete objects in JavaScript.
If that sounds like a memory leak waiting to happen then don’t worry, it shouldn’t be the case. Objects declared in functions will be eligible for garbage collection when they go out of scope, and you should avoid polluting the global namespace with objects in the first place. This also doesn’t really mean that delete is useless – it’s designed for deleting properties rather than objects, and that’s what you should use it for.
This is part three of a series of posts about JavaScript quirks. For part two, click here.
You do realize that delete is not meant to delete variables, right? It’s only meant to delete properties. You should understand the operator before you say that JS is hard… https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/delete
delete a // wrong but firefox assumes window.a
delete a.b // correct
You can use it to delete global variables by doing
delete window.a
But you can never use it to delete local variables.
“The delete operator is for deleting properties, not objects.”
You should read the article before pointing out errors. Heck, it’s even in bold.
あなたのテーマが魅力的だった。
A great point about the eval in the Developer tools. I was observing discrepancies that could only be explained by this. Thank you very much.
In example 2 (cat-dog-fish #2), you must be careful while using the keyword “this” because in another context it could definitely cause issues. In this case, it works, but I personally would use “window” just to be safe.
var example = new function e() {
this.name = ‘An example function’; // “this” refers to the object, not the window (example.name)
}
console.log(name); // name is undefined
console.log(example.name); // returns ‘An example function’
I used a bad example. I forgot window.name is an actual property of the window.
var example = new function() {
// “this” refers to example
// and sets example.property
this.property = ‘hello world’;
}
console.log(property); // undefined
console.log(example.property); // returns ‘hello world’