JavaScript is Hard Part 3: You Can’t Delete With Delete


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?

  1. a) fish, fish (success)
  2. b) fish, cat (failure)
  3. c) a different combination
  4. d) depends on the browser (always a good option if you’re guessing)
  5. 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
console.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:

  1. 1. Global code
  2. 2. Function code
  3. 3. 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.

Related Posts with Thumbnails

12 Comments

Leave a Comment

*
To prove you're a person (not a spam script), type the security word shown in the picture. Click on the picture to hear an audio file of the word.
Anti-spam image