0

There's already a similar question, and its answer is:

typeof yourVariable === 'object' && yourVariable !== null

However, that would also include HTML elements like document.body. Is there a way to detect if a value is a plain object?

Edit: By "plain object", I mean "user-created" objects. I don't want to match class instances, for example, or DOM elements. Just objects used as hashes, as @VLAZ suggested in the comments.

dodov
  • 5,206
  • 3
  • 34
  • 65
  • 2
    Why do you not consider `document.body` a plain object? What exactly is the discriminating factor you are looking for? – trincot Jul 02 '21 at 06:47
  • just check [lodash.isPlainObject](https://github.com/lodash/lodash/blob/master/isPlainObject.js) – banyudu Jul 02 '21 at 06:50
  • 2
    Is `{ get x() { return Math }}` a plain object? (I have hundreds of other questions like that). What makes an object "plain"? Please define the term unambiguously. – trincot Jul 02 '21 at 06:52
  • @trincot I think an even more interesting is `a = { foo: 1}` - that I have no doubt is considered plain. But then what about `b = Object.create(a)`? An object with a plain object as its prototype. Whether or not it's considered plain is a bit of a philosophical question at that stage. For practical reasons, I'd personally say class objects whose prototype is `Object.prototype` or `null` as plain. I'm aware that there are false positives and false negatives both with this heuristic but it's going to be correct the majority of the time. EDIT: also no proxies. – VLAZ Jul 02 '21 at 06:58
  • Maybe it'd be more accurate to say I need to check for object literals, i.e. "objects created with curly braces in source code." – dodov Jul 02 '21 at 07:29
  • 1
    Why would "object literals" matter at all? What is the difference between `x = {}; x.foo = 1;` and `x = Object.create(null); x.foo = 1` and `x = Object.create({foo: 1})`? – VLAZ Jul 02 '21 at 07:40
  • @VLAZ you're a lot more likely to create `x = { foo: 1 }` in your source code, rather than `x = Object.create(null); x.foo = 1`. For my use case, I care about objects created in source code. – dodov Jul 03 '21 at 05:05
  • `Object.create(null)` is a common enough pattern to create an object with no inherited properties. You can use that or `{}` as a hash to store some values, but `Object.create(null)` serves as a pristine one that doesn't carry over stuff such as `constructor` or `toString`, etc. The only properties and values it has are the ones you put there. So, if you're looking for "object used as a bag of properties", you'd be skipping something that's literally that. Object literals are not special in any way, you're likely trying to find objects within particular parameters. – VLAZ Jul 03 '21 at 06:28
  • @VLAZ I'm trying to test for "user-created" objects. I don't want to match class instances, for example, or DOM elements. I think `Object.getPrototypeOf(input) === null || Object.getPrototypeOf(input) === Object.prototype` will suffice? – dodov Jul 09 '21 at 06:10
  • 1
    It will work *most of the time*. I'd say it's probably enough. However, do be aware that there are cases it might not - [see my answer here](https://stackoverflow.com/a/63228981). The false positives/negatives you'd get are mostly edge cases. You could just accept it and use that. If you need more strictness, then it's going to be much harder to detect them. And if some user-made object is actively trying to hide itself you might not even have a way to detect that. However, again, these tend to be edge cases. I think that in most cases won't even matter if you just want objects as hashes. – VLAZ Jul 09 '21 at 06:16

1 Answers1

0

My guess would be to also check if the value's constructor is Object. Does that have any potential issues, though?

function check(value) {
  return typeof value === 'object' && value !== null && value.constructor === Object;
}

console.log(check({foo: 42}));
console.log(check(document.body));
dodov
  • 5,206
  • 3
  • 34
  • 65
  • 3
    `Object.create(null)` also creates a plain object but has no prototype and no constructor. – VLAZ Jul 02 '21 at 06:43
  • 4
    Also, `value.constructor` should be avoided, because `{ constructor: "LEGO" }` would mess up that result. Instead get it from the prototype `Object.getPrototypeOf(value).constructor`. That's just in general when you *need* the constructor. In this case, it's enough to just check if the prototype is `Object.prototype` or `null`. – VLAZ Jul 02 '21 at 06:46
  • What about `{ x: document.body}` or `Object.assign({}, document.body)`? – trincot Jul 02 '21 at 06:54
  • 1
    _"My guess..."_ - A _guess_ is not an answer – Andreas Jul 02 '21 at 06:54
  • @Andreas although I agree, if it wasn't a guess, I wouldn't have posted the question at all. I thought it might be better to post my theory as an answer and have it critiqued, rather than making the question longer than it has to be. Maybe I was wrong. – dodov Jul 02 '21 at 07:21