JavaScript / Beware the delete
Unlike C/C++, the delete◹ operator in JavaScript has nothing to do with memory deallocation.
What it actually does is remove a property from its parent object. If there is no more reference to that property, it will be marked as unreachable◹ and will be released when the garbage collector (GC) runs.
In the following example, delete foo.b
will mark the string “yolo” as unreachable, and it will eventually be released from the memory:
let foo = {
a: 12,
b: "yolo"
};
delete foo.b;
// foo = { a: 12 }
But in a scenario when two objects shared the same reference, things might not work as you expected.
let boo = { c: true };
let foo = {
a: 12,
b: boo
};
let bar = {
b: boo
};
// foo = { a: 12, b: { c: true } }
// bar = { b: { c: true } }
delete foo.b;
// foo = { a: 12 }
// bar = { b: { c: true } }
The string boo
is shared between foo
and bar
as the property b
, when you delete the reference foo.b
, the string boo
is still being referenced in bar.b
, so it’s still reachable. Hence, nothing will be deleted when the GC runs.
In strict mode, when the delete
command is called and the target could not be deleted, a SyntaxError
will be thrown. If running in non-strict mode (also known as sloopy mode◹), a false
value will be returned instead.
When a property is created as non-configurable◹, it cannot be deleted, and a TypeError
will be raised instead.
'use strict';
var bar = {};
Object.defineProperty(bar, 'hello', { configurable: false });
delete bar.hello; // TypeError
var foo = {};
Object.defineProperty(foo, 'goodbye', { configurable: true });
delete foo.goodbye; // this works fine
A variable defined with var
is either belongs to a scope of the function it’s defined in, or the global scope (if not defined inside any function).
When defined in the global scope, a variable is added as a non-configurable property to the global object. So, it cannot be deleted.
'use strict';
// global scope
var hello = "world";
delete globalThis.hello; // Type Error
delete hello; // Syntax Error, see below
Also, in strict mode, if delete
is used on a direct reference to a variable, function argument, or a function name, it also throws a Syntax Error:
'use strict';
var b = 'xyz';
delete b; // Syntax Error
let c = "hello";
delete c; // SyntaxError:
const d = 15;
delete d; // SyntaxError
function hello(x) {
delete x; // Syntax Error
}
function hello() {}
delete hello; // Syntax Error
Deleting a variable defined with var
inside a function is also does not works:
'use strict';
function hello() {
var x = "what's up?";
delete x; // Syntax Error
}
In non-strict mode, if you define a variable without the var
keyword, it will be added to the global object as configurable, hence, we can delete it:
awesome = true;
delete awesome; // true
And if you delete something that does not exist, it also returns true!
delete somethingDoesNotExist; // true!!!
Did you hate the delete
yet? Let me make you hate it more.
If you delete
an element from an array, it will be gone, but the length of the array is unchanged:
let arr = ['a', 'b', 'c', 'd', 'e', 'f'];
<shape of arr> = {
0: 'a', 1: 'b', 2: 'c',
3: 'd', 4: 'e', 5: 'f',
length: 6
}
delete arr[3];
<shape of arr> = {
0: 'a', 1: 'b', 2: 'c',
4: 'e', 5: 'f',
length: 6
}
This makes perfect sense based on the way delete
works, but it will be a lot of pain if someone has to debug array issues because you deleted an element from the array this way.
The right way to remove an element from an array is either to set it to undefined
or use Array.prototype.splice
:
arr[3] = undefined;
<shape of arr> = {
0: 'a', 1: 'b', 2: 'c',
3: undefined, 4: 'e', 5: 'f',
length: 6
}
arr.splice(3, 1);
<shape of arr> = {
0: 'a', 1: 'b', 2: 'c',
3: 'e', 4: 'f',
length: 5
}