It is common advise to nullify a prop value within an object rather than deleting the prop. Should you prefer one over the other? Especially, in performance intensive applications.
Delete object prop
What do you do when you do not need a key-value pair in an object? Why, we simply delete
it, of course.
1
2
3
4
5
6
7
8
9
|
const earth = { name: "earth", position: 3, life: true };
console.log(earth);
// { name: 'earth', position: 3, life: true }
delete earth["life"];
console.log(earth);
// { name: 'earth', position: 3 }
|
This is clean and just works.
Except that the compiler/interpreter has to do so much extra work to delete a prop within an object. When the object changes, Javascript will recompile “associated” code internal to the compiler and reset things are required. This requires more computation and time - especially if you have large number of elements.
At least, in theory.
What you can do instead - null
the prop
You can set key value to undefined
or null
in the object.
1
2
3
4
5
6
7
8
9
|
const earth = { name: "earth", position: 3, life: true };
console.log(earth);
// { name: 'earth', position: 3, life: true }
earth["life"] = null;
console.log(earth);
// { name: 'earth', position: 3, life: null }
|
I often use undefined
instead of null
since I can expect props to be actual nulls at one time or the other.
A Quick Test
Consider below code that deletes or nullifies prop value for a 1000 elements in an object.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
const arr = new Array(1e5).fill("hello");
const obj = { ...arr };
console.time("del");
Object.entries(obj).forEach(ele => delete obj[ele]);
console.timeEnd("del");
console.time("null");
Object.entries(obj).forEach(ele => (obj[ele] = null));
console.timeEnd("null");
/*
del: 184.942ms
null: 221.430ms
del: 200.653ms
null: 229.400ms
del: 199.422ms
null: 268.564ms
*/
|
The quick and dirty test does not reveal anything significant in a set of elements in a loop - but surprisingly delete
is better than nullifying a prop value.
Let us transfer the time measurement to the individual transactions rather than the entire loop. We will take an average of all observed times to output average performance.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
const { performance } = require("perf_hooks");
const arr = new Array(1000).fill("hello");
const obj = { ...arr };
const delTimes = [];
const nullTimes = [];
let start;
Object.entries(obj).forEach(ele => {
start = performance.now();
delete obj[ele];
delTimes.push(performance.now() - start);
});
console.log(
"del avg: ",
delTimes.reduce((sum, val) => (sum += val)) / delTimes.length
);
Object.entries(obj).forEach(ele => {
start = performance.now();
obj[ele] = null;
nullTimes.push(performance.now() - start);
});
console.log(
"null avg: ",
nullTimes.reduce((sum, val) => (sum += val)) / nullTimes.length
);
/*
del avg: 0.015970081090927124
null avg: 0.014829903841018677
del avg: 0.009639912843704223
null avg: 0.01293981671333313
del avg: 0.012559592723846436
null avg: 0.006950092315673828
*/
|
‘delete` still has an upper hand - but nothing “significant”.
If we increase the array size to 1e5
-
1
2
3
4
5
6
7
8
9
10
|
/*
del avg: 0.0019049220073223115
null avg: 0.0018892455852031708
del avg: 0.0019672254502773284
null avg: 0.0022597119933366774
del avg: 0.0024934078419208525
null avg: 0.0036383388513326646
*/
|
Nope - not quite what I expected. But, here we are.
Conclusion
I will continue using delete
until the time I understand the concepts much better than what I do now.