0
function isEmpty(obj) {
  for(var key in obj) {
    return false;
    }
    return true;
}

What is wrong with this way of checking if an object is empty? I'm fairly new to JS and I'm going through SO to check what is the best and fastest way of checking if an object is empty and came across is this How do I test for an empty JavaScript object? among others.

All questions have similar answers checking Object.prototype.hasOwnProperty which seems unnecessarily complicated. Is there a use-case where the code I've provided won't work and I should use the answers provided in the linked questions?

Jared Smith
  • 19,721
  • 5
  • 45
  • 83
NoobScript
  • 47
  • 1
  • 8
  • 3
    [There's no such thing as a "JSON Object"](http://benalman.com/news/2010/03/theres-no-such-thing-as-a-json/) – Andreas Oct 19 '21 at 11:47
  • 2
    May I ask, why you need to check if an object is empty? In my experience that is a quite rare thing to do. – RoToRa Oct 19 '21 at 11:49
  • 2
    Are you aware of the difference between `"x" in obj` and `obj.hasOwnProperty("x")`? See [How do I check if an object has a specific property in JavaScript?](/q/135448/4642212). – Sebastian Simon Oct 19 '21 at 11:49
  • 1
    I'm not sure I understand. You don't need to know if an object is empty to search for a key in it. – RoToRa Oct 19 '21 at 11:52
  • 1
    Then an explicit test for emptiness is unnecessary... Just iterate over the properties. – Andreas Oct 19 '21 at 11:52
  • 1
    You can get keys as array with keys() function. let objKeys = obj.keys(). And check values of keys – Dostonbek Oripjonov Oct 19 '21 at 11:52
  • 1
    There are a lot of things wrong with that approach. 1. it walks the prototype chain, which is almost certainly not what you want. 2. it does not test for non-enumerable properties, which is *probably* not what you want. If it's something you're using for your own code and you understand the limitations fine, but if you're offering it to others as a library function it misses a lot of edge cases and it's slow. Which is presumably why the prior art you reference is "unnecessarily complicated". – Jared Smith Oct 19 '21 at 11:53
  • 2
    "Unnecessarily complicated" for one person is "not complicated enough" for someone else. You asked for use cases where the code you've provided won't work and are shooting down people who offer exactly those use cases. Decide if you want simplicity without covering edge cases and ignore the edge cases, or allow complications to catch the edge cases. – Heretic Monkey Oct 19 '21 at 11:58
  • You need to give a more concrete example. Accessing deeply nested elements doesn't change if the objects are empty or not. What makes a difference is, if the keys exist or not. – RoToRa Oct 19 '21 at 12:00
  • _“Can you elaborate what is ‘walks the prototype chain’ and why i wouldn't want it?”_ — You said that you were aware of how `in` differs from `hasOwnProperty`. Evidently not. See the documentation on [`for`–`in`](//developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/for...in#iterating_over_own_properties_only) and the related docs on the [`in` operator](//developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/in#inherited_properties). Read about [Enumerability and ownership of properties](//developer.mozilla.org/docs/Web/JavaScript/Enumerability_and_ownership_of_properties). – Sebastian Simon Oct 19 '21 at 12:02
  • Coming to think of it, I think what you are looking for is the optional chaining operator: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining However it's only supported by newer JS implementations and you may need a transcompiler. – RoToRa Oct 19 '21 at 12:07
  • 1
    @NoobScript posted as an answer, too long for a comment. – Jared Smith Oct 19 '21 at 12:07

1 Answers1

0

Consider the following case:

const foo = { a: 1 }
const bar = {}
Object.setPrototypeOf(bar, foo)
console.log(bar) // {}
for (let key in bar) console.log(key) // logs a!!

That is what I meant in the comment about walking the prototype chain.

Consider also this:

const foo = {}
Object.defineProperty(foo, 'a', {
  enumerable: false,
  value: 1,
})
console.log(foo.a) // 1
for (let key in foo) console.log(key) // doesn't log anything!

So that's two edge cases your function misses. You probably don't want to log anything in the first example, and you probably do want to log the key in the second.

Jared Smith
  • 19,721
  • 5
  • 45
  • 83
  • @NoobScript no problem. On a more meta level, the `for...in` loop is IMHO a footgun in the language, I would avoid its use unless you are really familiar with the semantics. The better alternatives are the static Object methods like `Object.keys`, `Object.values`, `Object.entries`, `Object.getOwnPropertyNames`, etc which have much clearer semantics. – Jared Smith Oct 19 '21 at 12:22