<div><div id="article__content"><div><ul><li>14 min read</li><li><a href="/category/javascript">JavaScript</a>,
<a href="/category/tools">Tools</a>,
<a href="/category/techniques">Techniques</a></li></ul><p><section aria-label="Quick summary"><span aria-hidden="true" id="article__start"></span>Removing properties from an object in JavaScript might not be the most exciting job, but there are many ways to achieve it, each revealing a fundamental aspect of how JavaScript works. Juan Diego Rodríguez explores each technique in this article.</section></p><p>A group of contestants are asked to complete the following task:</p><blockquote>Make <code>object1</code> similar to <code>object2</code>.</blockquote><pre><code>let object1 = {
  a: "hello",
  b: "world",
  c: "!!!",
};

let object2 = {
  a: "hello",
  b: "world",
};
</code></pre><p>Seems easy, right? Simply delete the <code>c</code> property to match <code>object2</code>. Surprisingly, each person described a different solution:</p><ul><li><strong>Contestant A:</strong> “I set <code>c</code> to <code>undefined</code>.â€</li><li><strong>Contestant B:</strong> “I used the <code>delete</code> operator.â€</li><li><strong>Contestant C:</strong> “I deleted the property through a <code>Proxy</code> object.â€</li><li><strong>Contestant D:</strong> “I avoided mutation by using object destructuring.â€</li><li><strong>Contestant E:</strong> “I used <code>JSON.stringify</code> and <code>JSON.parse</code>.â€</li><li><strong>Contestant F:</strong> “We rely on Lodash at my company.â€</li></ul><p>An awful lot of answers were given, and they all seem to be valid options. So, who is “rightâ€? Let’s dissect each approach.</p><h2 id="contestant-a-i-set-c-to-undefined">Contestant A: “I Set <code>c</code> To <code>undefined</code>.â€</h2><p>In JavaScript, accessing a non-existing property returns <code>undefined</code>.</p><pre><code>const movie = {
  name: "Up",
};

console.log(movie.premiere); // undefined
</code></pre><p>It’s easy to think that setting a property to <code>undefined</code> removes it from the object. But if we try to do that, we will observe a small but important detail:</p><pre><code>const movie = {
  name: "Up",
  premiere: 2009,
};

movie.premiere = undefined;

console.log(movie);
</code></pre><p>Here is the output we get back:</p><pre><code>{name: 'up', premiere: undefined}
</code></pre><p>As you can see, <code>premiere</code> still exists inside the object even when it is <code>undefined</code>. This approach doesn’t actually delete the property but rather changes its value. We can confirm that using the <code>hasOwnProperty()</code> method:</p><pre><code>const propertyExists = movie.hasOwnProperty("premiere");

console.log(propertyExists); // true
</code></pre><p>But then why, in our first example, does accessing <code>object.premiere</code> return <code>undefined</code> if the property doesn’t exist in the object? Shouldn’t it throw an error like when accessing a non-existing variable?</p><pre><code>console.log(iDontExist);

// Uncaught ReferenceError: iDontExist is not defined
</code></pre><p>The answer lies in how <code>ReferenceError</code> behaves and what a reference is in the first place.</p><blockquote>A <strong>reference</strong> is a resolved name binding that indicates where a value is stored. It consists of three components: a <strong>base value</strong>, the <strong>referenced name</strong>, and a <strong>strict reference flag</strong>.</blockquote><p>For a <code>user.name</code> reference, the base value is the object, <code>user</code>, while the referenced name is the string, <code>name</code>, and the strict reference flag is <code>false</code> if the code isn’t in <code>strict mode</code>.</p><p>Variables behave differently. They don’t have a parent object, so their base value is an <em>environment record</em>, i.e., a unique base value assigned each time the code is executed.</p><p>If we try to access something that doesn’t have a base value, JavaScript will throw a <code>ReferenceError</code>. However, if a base value is found, but the referenced name doesn’t point to an existing value, JavaScript will simply assign the value <code>undefined</code>.</p><blockquote>“The Undefined type has exactly one value, called <strong>undefined</strong>. Any variable that has not been assigned a value has the value <strong>undefined</strong>.â€<p>— <a href="https://262.ecma-international.org/14.0/#sec-ecmascript-language-types-undefined-type">ECMAScript Specification</a></p></blockquote><p>We could spend an entire article just addressing <code>undefined</code> shenanigans!</p><h2 id="contestant-b-i-used-the-delete-operator">Contestant B: “I Used The <code>delete</code> Operator.â€</h2><p>The <code>delete</code> operator’s sole purpose is to remove a property from an object, returning <code>true</code> if the element is successfully removed.</p><pre><code>const dog = {
  breed: "bulldog",
  fur: "white",
};

delete dog.fur;

console.log(dog); // {breed: 'bulldog'}
</code></pre><p>Some caveats come with the <code>delete</code> operator that we have to take into consideration before using it. First, the <code>delete</code> operator can be used to remove an element from an array. However, it leaves an empty slot inside the array, which may cause unexpected behavior since properties like <code>length</code> aren’t updated and still count the open slot.</p><pre><code>const movies = ["Interstellar", "Top Gun", "The Martian", "Speed"];

delete movies[2];

console.log(movies); // ['Interstellar', 'Top Gun', empty, 'Speed']

console.log(movies.length); // 4
</code></pre><p>Secondly, let’s imagine the following nested object:</p><pre><code>const user = {
  name: "John",
  birthday: {day: 14, month: 2},
};
</code></pre><p>Trying to remove the <code>birthday</code> property using the <code>delete</code> operator will work just fine, but there is a common misconception that doing this frees up the memory allocated for the object.</p><p>In the example above, <code>birthday</code> is a property holding a nested object. Objects in JavaScript behave differently from primitive values (e.g., numbers, strings, and booleans) as far as how they are stored in memory. They are stored and copied “by reference,â€ while primitive values are copied independently as a whole value.</p><p>Take, for example, a primitive value such as a string:</p><pre><code>let movie = "Home Alone";
let bestSeller = movie;
</code></pre><p>In this case, each variable has an independent space in memory. We can see this behavior if we try to reassign one of them:</p><pre><code>movie = "Terminator";

console.log(movie); // "Terminator"

console.log(bestSeller); // "Home Alone"
</code></pre><p>In this case, reassigning <code>movie</code> doesn’t affect <code>bestSeller</code> since they are in two different spaces in memory. Properties or variables holding objects (e.g., regular objects, arrays, and functions) are references pointing to a single space in memory. <strong>If we try to copy an object, we are merely duplicating its reference.</strong></p><pre><code>let movie = {title: "Home Alone"};
let bestSeller = movie;

bestSeller.title = "Terminator";

console.log(movie); // {title: "Terminator"}

console.log(bestSeller); // {title: "Terminator"}
</code></pre><p>As you can see, they are now objects, and reassigning a <code>bestSeller</code> property also changes the <code>movie</code> result. Under the hood, JavaScript looks at the actual object in memory and performs the change, and both references point to the changed object.</p><p>Knowing how objects behave “by reference,â€ we can now understand how using the <code>delete</code> operator doesn’t free space in memory.</p><p>The process in which programming languages free memory is called <a href="https://developer.mozilla.org/en-US/docs/Glossary/Garbage_collection"><em>garbage collection</em></a>. In JavaScript, memory is freed for an object when there are no more references and it <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_management#garbage_collection">becomes unreachable</a>. So, using the <code>delete</code> operator may make the property’s space eligible for collection, but there may be more references preventing it from being deleted from memory.</p><p>While we’re on the topic, it’s worth noting that there is a bit of a <a href="https://stackoverflow.com/questions/59431589/javascript-performance-constraints-of-delete-keyword">debate around the <code>delete</code> operator’s impact on performance</a>. You can follow the rabbit trail from the link, but I’ll go ahead and spoil the ending for you: the difference in performance is so negligible that it wouldn’t pose a problem in the vast majority of use cases. Personally, I consider the operator’s idiomatic and straightforward approach a win over a minuscule hit to performance.</p><p>That said, an argument can be made against using <code>delete</code> since it mutates an object. In general, it’s a good practice to avoid mutations since they may lead to unexpected behavior where a variable doesn’t hold the value we assume it has.</p><h2 id="contestant-c-i-deleted-the-property-through-a-proxy-object">Contestant C: “I Deleted The Property Through A <code>Proxy</code> Object.â€</h2><p>This contestant was definitely a show-off and used a proxy for their answer. A proxy is a way to insert some middle logic between an object’s common operations, like getting, setting, defining, and, yes, deleting properties. It works through the <code>Proxy</code> constructor that takes two parameters:</p><ul><li><strong><code>target</code>:</strong> The object from where we want to create a proxy.</li><li><strong><code>handler</code>:</strong> An object containing the middle logic for the operations.</li></ul><p>Inside the <code>handler</code>, we define methods for the different operations, called <em>traps</em>, because they intercept the original operation and perform a custom change. The constructor will return a <code>Proxy</code> object — an object identical to the <code>target</code> — but with the added middle logic.</p><pre><code>const cat = {
  breed: "siamese",
  age: 3,
};

const handler = {
  get(target, property) {
    return `cat's ${property} is ${target[property]}`;
  },
};

const catProxy = new Proxy(cat, handler);

console.log(catProxy.breed); // cat's breed is siamese

console.log(catProxy.age); // cat's age is 3
</code></pre><p>Here, the <code>handler</code> modifies the getting operation to return a custom value.</p><p>Say we want to log the property we are deleting to the console each time we use the <code>delete</code> operator. We can add this custom logic through a proxy using the <code>deleteProperty</code> trap.</p><pre><code>const product = {
  name: "vase",
  price: 10,
};

const handler = {
  deleteProperty(target, property) {
    console.log(`Deleting property: ${property}`);
  },
};

const productProxy = new Proxy(product, handler);

delete productProxy.name; // Deleting property: name
</code></pre><p>The name of the property is logged in the console but throws an error in the process:</p><div><pre><code>Uncaught TypeError: 'deleteProperty' on proxy: trap returned falsish for property 'name'
</code></pre></div><p>The error is thrown because the handler didn’t have a <code>return</code> value. That means it defaults to <code>undefined</code>. In strict mode, if the <code>delete</code> operator returns <code>false</code>, it will throw an error, and <code>undefined</code>, being a falsy value, triggers this behavior.</p><p>If we try to return <code>true</code> to avoid the error, we will encounter a different sort of issue:</p><pre><code>// ...

const handler = {
  deleteProperty(target, property) {
    console.log(`Deleting property: ${property}`);

    return true;
  },
};

const productProxy = new Proxy(product, handler);

delete productProxy.name; // Deleting property: name

console.log(productProxy); // {name: 'vase', price: 10}
</code></pre><p>The property isn’t deleted!</p><p>We replaced the <code>delete</code> operator’s default behavior with this code, so it doesn’t remember it has to “deleteâ€ the property.</p><p>This is where <code>Reflect</code> comes into play.</p><blockquote><code>Reflect</code> is a global object with a collection of all the internal methods of an object. Its methods can be used as normal operations anywhere, but it’s meant to be used inside a proxy.</blockquote><p>For example, we can solve the issue in our code by returning <code>Reflect.deleteProperty()</code> (i.e., the <code>Reflect</code> version of the <code>delete</code> operator) inside of the handler.</p><pre><code>const product = {
  name: "vase",
  price: 10,
};

const handler = {
  deleteProperty(target, property) {
    console.log(`Deleting property: ${property}`);

    return Reflect.deleteProperty(target, property);
  },
};

const productProxy = new Proxy(product, handler);

delete productProxy.name; // Deleting property: name

console.log(product); // {price: 10}
</code></pre><p>It is worth calling out that certain objects, like <code>Math</code>, <code>Date</code>, and <code>JSON</code>, have properties that cannot be deleted using the <code>delete</code> operator or any other method. These are “non-configurableâ€ object properties, meaning that they cannot be reassigned or deleted. If we try to use the <code>delete</code> operator on a non-configurable property, it will fail silently and return <code>false</code> or throw an error if we are running our code in strict mode.</p><pre><code>"use strict";

delete Math.PI;
</code></pre><p>Output:</p><pre><code>Uncaught TypeError: Cannot delete property 'PI' of #&lt;Object&gt;
</code></pre><p>If we want to avoid errors with the <code>delete</code> operator and non-configurable properties, we can use the <code>Reflect.deleteProperty()</code> method since it doesn’t throw an error when trying to delete a non-configurable property — even in strict mode — because it fails silently.</p><p>I assume, however, that you would prefer knowing when you are trying to delete a global object rather than avoiding the error.</p><h2 id="contestant-d-i-avoided-mutation-by-using-object-destructuring">Contestant D: “I Avoided Mutation By Using Object Destructuring.â€</h2><blockquote><strong>Object destructuring</strong> is an assignment syntax that extracts an object’s properties into individual variables. It uses a curly braces notation (<code>{}</code>) on the left side of an assignment to tell which of the properties to get.</blockquote><pre><code>const movie = {
  title: "Avatar",
  genre: "science fiction",
};

const {title, genre} = movie;

console.log(title); // Avatar

console.log(genre); // science fiction
</code></pre><p>It also works with arrays using square brackets (<code>[]</code>):</p><pre><code>const animals = ["dog", "cat", "snake", "elephant"];

const [a, b] = animals;

console.log(a); // dog

console.log(b); // cat
</code></pre><p>The spread syntax (<code>...</code>) is sort of like the opposite operation because it encapsulates several properties into an object or an array if they are single values.</p><p>We can use object destructuring to unpack the values of our object and the spread syntax to keep only the ones we want:</p><pre><code>const car = {
  type: "truck",
  color: "black",
  doors: 4
};

const {color, ...newCar} = car;

console.log(newCar); // {type: 'truck', doors: 4}
</code></pre><p>This way, we avoid having to mutate our objects and the potential side effects that come with it!</p><p>Here’s an edge case with this approach: deleting a property only when it’s <code>undefined</code>. Thanks to the flexibility of object destructuring, we can delete properties when they are <code>undefined</code> (or <a href="https://developer.mozilla.org/en-US/docs/Glossary/Falsy">falsy</a>, to be exact).</p><p>Imagine you run an online store with a vast database of products. You have a function to find them. Of course, it will need some parameters, perhaps the product name and category.</p><pre><code>const find = (product, category) =&gt; {
  const options = {
    limit: 10,
    product,
    category,
  };

  console.log(options);

  // Find in database...
};
</code></pre><p>In this example, the product <code>name</code> has to be provided by the user to make the query, but the <code>category</code> is optional. So, we could call the function like this:</p><pre><code>find("bedsheets");
</code></pre><p>And since a <code>category</code> is not specified, it returns as <code>undefined</code>, resulting in the following output:</p><pre><code>{limit: 10, product: 'beds', category: undefined}
</code></pre><p>In this case, we shouldn’t use <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters">default parameters</a> because we aren’t looking for one specific category.</p><p>Notice how the database could incorrectly assume that we are querying products in a category called <code>undefined</code>! That would lead to an empty result, which is an unintended side effect. Even though many databases will filter out the <code>undefined</code> property for us, it would be better to sanitize the options before making the query. A cool way to dynamically remove an <code>undefined</code> property is through object destructing along with the <code>AND</code> operator (<code>&amp;&amp;</code>).</p><p>Instead of writing <code>options</code> like this:</p><pre><code>const options = {
  limit: 10,
  product,
  category,
};
</code></pre><p>&amp;mldr;we can do this instead:</p><pre><code>const options = {
  limit: 10,
  product,
  ...(category &amp;&amp; {category}),
};
</code></pre><p>It may seem like a complex expression, but after understanding each part, it becomes a straightforward one-liner. What we are doing is taking advantage of the <code>&amp;&amp;</code> operator.</p><p>The <code>AND</code> operator is mostly used in conditional statements to say,</p><blockquote>If <code>A</code> and <code>B</code> are <code>true</code>, then do this.</blockquote><p>But at its core, it evaluates two expressions from left to right, returning the expression on the left if it is <em>falsy</em> and the expression on the right if they are both <em>truthy</em>. So, in our prior example, the <code>AND</code> operator has two cases:</p><ol><li><code>category</code> is <code>undefined</code> (or <em>falsy</em>);</li><li><code>category</code> is defined.</li></ol><p>In the first case where it is <em>falsy</em>, the operator returns the expression on the left, <code>category</code>. If we plug <code>category</code> inside the rest of the object, it evaluates this way:</p><pre><code>const options = {
  limit: 10,

  product,

  ...category,
};
</code></pre><p>And if we try to destructure any falsy value inside an object, they will be destructured into nothing:</p><pre><code>const options = {
  limit: 10,
  product,
};
</code></pre><p>In the second case, since the operator is <em>truthy</em>, it returns the expression on the right, <code>{category}</code>. When plugged into the object, it evaluates this way:</p><pre><code>const options = {
  limit: 10,
  product,
  ...{category},
};
</code></pre><p>And since <code>category</code> is defined, it is destructured into a normal property:</p><pre><code>const options = {
  limit: 10,
  product,
  category,
};
</code></pre><p>Put it all together, and we get the following <code>betterFind()</code> function:</p><pre><code>const betterFind = (product, category) =&gt; {
  const options = {
    limit: 10,
    product,
    ...(category &amp;&amp; {category}),
  };

  console.log(options);

  // Find in a database...
};

betterFind("sofas");
</code></pre><p>And if we don’t specify any <code>category</code>, it simply does not appear in the final <code>options</code> object.</p><pre><code>{limit: 10, product: 'sofas'}
</code></pre><h2 id="contestant-e-i-used-json-stringify-and-json-parse">Contestant E: “I Used <code>JSON.stringify</code> And <code>JSON.parse</code>.â€</h2><p>Surprisingly to me, there is a way to remove a property by reassigning it to <code>undefined</code>. The following code does exactly that:</p><pre><code>let monitor = {
  size: 24,
  screen: "OLED",
};

monitor.screen = undefined;

monitor = JSON.parse(JSON.stringify(monitor));

console.log(monitor); // {size: 24}
</code></pre><p>I sort of lied to you since we are employing some JSON shenanigans to pull off this trick, but we can learn something useful and interesting from them.</p><p>Even though JSON takes direct inspiration from JavaScript, it differs in that it has a strongly typed syntax. It doesn’t allow functions or <code>undefined</code> values, so using <code>JSON.stringify()</code> will omit all non-valid values during conversion, resulting in JSON text without the <code>undefined</code> properties. From there, we can parse the JSON text back to a JavaScript object using the <code>JSON.parse()</code> method.</p><p>It’s important to know the limitations of this approach. For example, <code>JSON.stringify()</code> skips functions and throws an error if either a circular reference (i.e., a property is referencing its parent object) or a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt"><code>BigInt</code></a> value is found.</p><h2 id="contestant-f-we-rely-on-lodash-at-my-company">Contestant F: “We Rely On Lodash At My Company.â€</h2><p>It’s worth noting that utility libraries such as Lodash.js, Underscore.js, or Ramda also provide methods to delete — or <code>pick()</code> — properties from an object. We won’t go through different examples for each library since their documentation already does an excellent job of that.</p><h2 id="conclusion">Conclusion</h2><p>Back to our initial scenario, which contestant is right?</p><p>The answer: <strong>All of them!</strong> Well, except for the first contestant. Setting a property to <code>undefined</code> just isn’t an approach we want to consider for removing a property from an object, given all of the other ways we have to go about it.</p><p>Like most things in development, the most “correctâ€ approach depends on the situation. But what’s interesting is that behind each approach is a lesson about the very nature of JavaScript. Understanding all the ways to delete a property in JavaScript can teach us fundamental aspects of programming and JavaScript, such as memory management, garbage collection, proxies, JSON, and object mutation. That’s quite a bit of learning for something seemingly so boring and trivial!</p><h4 id="further-reading-on-smashingmag">Further Reading On SmashingMag</h4><ul><li>“<a href="https://www.smashingmagazine.com/2023/02/discovering-primitive-objects-javascript-part1/">Discovering Primitive Objects In JavaScript (Part 1)</a>,â€ Kirill Myshkin</li><li>“<a href="https://www.smashingmagazine.com/2023/06/primitive-objects-javascript-part-2/">Primitive Objects In JavaScript: When To Use Them (Part 2)</a>,â€ Kirill Myshkin</li><li>“<a href="https://www.smashingmagazine.com/2019/09/reintroduction-destructuring-assignment/">A Re-Introduction To Destructuring Assignment</a>,â€ Laurie Barth</li><li>“<a href="https://www.smashingmagazine.com/2022/11/document-object-model-geometry-guide/">Document Object Model (DOM) Geometry: A Beginner’s Introduction And Guide</a>,â€ Pearl Akpan</li></ul><p><img alt="Smashing Editorial" decoding="async" height="46" loading="lazy" src="https://www.smashingmagazine.com/images/logo/logo--red.png" width="35"></img>
<span>(gg, yk)</span></p></div></div></div>