-5

I'm my scenario I have to create a lot of objects (100k+). Those objects can 'evolve' in the course if their life. Some will stay practically unchanged, others with will mutate and have a bunch of props assigned.

The most evolved objects might have 30+ properties.

All object are just data, no function, no logic.

From performance point of view, should I use:

const obj = Object.create(null)

obj.prop1 = ...;
obj.prop2 = ...;

//later

obj.prop3 = ...;

//later still

obj.prop4 = ...;

or

cost obj = {
    prop1 = ...;
    prop2 = ...;

    prop3 = undefined;
    prop4 = undefined;
}

Or just define the class and use new.

I'm considering the first approach because:

  1. there might be some memory savings with null prototype (and smaller hashtable?)
  2. it is nicer to iterate over object properties without doing 'hasOwnProperty'.
  3. Also it will be easier for me to debug without a bunch of undefined properties hanging on the list in IDE/DevTools.

At the same time, I vaguely remember that v8 was doing some JIT magic with compilation of objects, but if I continue to add properties overtime, that this optimization could go out of the window.

Which option is better from memory usage and/or performance point of view?

3
  • If prop3 might not be needed, not creating it is almost certainly better performance-wise. It's hard to imagine how doing something can be faster than not doing it. The difference between using an object literal and assigning properties with assignments is probably negligible, but you should benchmark to be sure. Commented Dec 23, 2024 at 23:09
  • 1
    If you care about IDE development, initializing all the properties is probably best, since the IDE will then be able to provide property completion. Even better would be to use ES6 classes. Commented Dec 23, 2024 at 23:11
  • of course, the cost obj = { etc is invalid syntax so that wouldn't work at all Commented Dec 24, 2024 at 0:50

1 Answer 1

5

(V8 developer here.)

For performance: use a class, and create all properties in the constructor, unconditionally.
A traditional constructor function works too; the key point is that all objects end up having the same shape (in particular: same set of properties). When that's the case, then the generated code for any, say, obj.prop3 property lookup can be specialized to that one object shape, which is faster than having to handle multiple object shapes (even if they all have .prop3 as their third property).

For memory: of course, smaller objects (with fewer properties) use less memory.
(Using null as the prototype has no impact on memory consumption in either direction, as the default Object prototype exists anyway, whether your objects use it or not.)

So ultimately, it's a tradeoff, and only you can decide which approach works best for your app. Depending on your requirements and your data's characteristics, it could in extreme cases even be worthwhile to use a hybrid approach, where e.g. prop1 through prop5 are always present because they're very common, and then there's an all_other_props property that points at a child object (or perhaps a Map) that holds additional rarely-used properties. But it's rare that this matters enough to be worth the hassle.

When in doubt, try both/several approaches and measure. And when you measure, be sure to measure with your real app, not just a microbenchmark -- microbenchmarks tend to either ignore or over-emphasize things that your real app doesn't care about, so they can easily trick you into making a decision that'll end up being suboptimal. If you can't measure a difference in your real app, then the difference doesn't matter.


Re comments:

If prop3 might not be needed, not creating it is almost certainly better performance-wise. It's hard to imagine how doing something can be faster than not doing it.

It's not about performance of object creation (where, sure, doing more work costs at least slightly more time), it's about performance of code handling those objects later, where (1) it's beneficial when all objects have the same shape, and (2) overwriting an existing property is cheaper than adding a new property.

Sign up to request clarification or add additional context in comments.

5 Comments

The first paragraph only applies if there are multiple objects like this one. If each instance is unique, it seems moot.
I like to use Object.setPrototypeOf({}, null) instead of Object.create(null) because it produces an instance with no prototype chain (there's not even a constructor property). But the resulting instance will not be Object compatible as it loses all of Object.prototype inheritance and will contain exclusively the properties set into them. Still, a question remains... Discarding the Object.prototype from the prototype chain will help save memory in long lived instances? Or is it irrelevant because it'll be only excluding a reference (very small data) in the instance hashtable?
@jmrk thanks a lot, marking as correct because it is very insightful even if it's 'it depends' at the end, but that is what I needed. I wanted my assumption checked to know which direction to experiment.
@Barmar true, but per OP we have "100K+" objects, and for performance you wouldn't want to have that many object shapes flying around if you can avoid it.
@KLASANGUI nope, nulling out the prototype saves nothing, because the Object.prototype exists as long as even a single object refers to it. And each object (well, each shape) needs an internal __proto__ slot either way, which can either point at null or at an actual prototype, so again it doesn't get smaller by setting that to null.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.