Don't stop Mutating
I recently came across a tweet by Simon Høiberg that basically forbids you to use delete
. The reason for this? "You don't want to mutate the existing object. It leads to inconsistent and unpredictable behavior"
This kind of advice turns me sad. I mean, the keyword is there. JavaScript allows us to delete properties from an object. Why not use it?
Don't get me wrong. There is a truth hidden in Simon's message. There are scenarios where you can easily avoid that keyword. And there are scenarios where mutating will cause trouble. The question is, do you really need to avoid it like the plague?
Immutable or Readable
The internet speaks about two primary reasons why you shouldn't be using delete
.
- Mutable, delete mutates an object which is bad. 1
- Performance, delete has serious performance impact. 2 3
Readability doesn't seem to be very important nowadays. So let me focus on that part.
But first, let's take a look at some code. I find it easier to talk that way. I've taken Simon's example. We have a number of users, and want to delete the age
property.
const users = await fetchUsers(100);
const newUsers = [];
for (let i = 0; i < users.length; i++) {
const { age, ...newUser } = users[i];
newUsers.push(newUser);
}
How was that? It's a quite basic snippet, so I hope it was easy to understand. The above, is the version that uses object destructuring and also pushes the users without the age to a new array. Because, if we don't want to mutate the user records, we also don't want to mutate the list. It wouldn't make much sense otherwise.
Now, please compare it to the next example, where I don't know any better, and simply mutate the data.
const users = await fetchUsers(100);
for (let i = 0; i < users.length; i++) {
delete users[i].age;
}
How was that for readability? I definitely prefer the last one. It's way easier to see what's going on. Sure, I understand the first one perfectly fine. That's not what this is about. The mutating variant simply adds less noise.
Unpredictable behavior
I can hear you think. But what about the "unpredictable behavior"?!. One example that I instantly can come up with where mutating can cause trouble, is in React. React uses mutations to detect when it should update the user interface (DOM). So yes, it's important there.
That being said, if you fetch a large object from a rest api, and wish to do some cleaning before you save the object in a state/store. Than why could it not be a mutating action?
Basically, if we take the example from above, and would wrap it in a function. What trouble can it give us?
async function getUsersWithoutProjects() {
const users = await fetchUsers(100);
for (let i = 0; i < users.length; i++) {
delete users[i].projects;
}
return users;
}
Do you have the answer? Right.., none! Because for the outside world, users
never had that property to start with. The data is created and mutated in the same boundary (/scope). Because users
never left this function with the projects attached, nothing can depend on it.
Performance
But what about performance?!! Well, are you deleting large values or small values? A single one, or thousands? How does the rest of your code perform? If you don't know, then don't worry about it. You can try to optimize till the latest ms, but if the data request takes hundreds of milliseconds, would that delete
call really make a difference?
I've created a simple perf.link that shows you that delete
doesn't need to be slower than the alternative. It is one case out of thousands of potential scenarios. All I'm saying is, it's not black and white. If you have an edge case, please do what feels best. I'm confident that there are cases where delete
is the performance bottleneck. But I'm just as confident that 99% of us, will never work on those kinds of projects.
Then the other thing about performance. Not regarding delete
, but regarding mutating. If it's about assigning new values to properties instead of reconstructing entire objects, mutating is seriously faster. Again, in most cases, reconstructing objects and working in an immutable way performs fine. You won't experience any slowness because of it. But in those other cases, mutating data is okay. Maybe even preferable.
Conclusion
I hope you liked this article. Because I'm not going to tell you if you should mutate your objects or not. Both mutable as well as immutable solutions have their time and place. Use them accordingly, and do what feels best. In most cases, go with what's easiest to read.
This article is another attempt of me to stop the "DON'T DO THIS" shouting on the internet. Programming isn't black and white. We can't just ban half the keywords or native functions because they "feel wrong". There is a valid use case for each and every function.