Ability to access property *without* prototype inheritance

Sometimes you want to access an own property only.

For example, sometimes you want to do the equivalent of

obj[key]

but without the prototype lookup happening so that

  • if the property is not an own property, then the result will be undefined (do not get the value from prototype)
  • if the property is an own property, then it returns the value

This would be especially useful when using Symbols as an alternative to WeakMap, for example, where we want to associate state per object reference and not have it shared with multiple objects via prototype.

const something = Symbol()

const obj = {}

obj[something] = 123

const obj2 = {__proto__: obj}

obj2[something]

In that sample, obj2[something] returns 123 because it looks the property up on the prototype (obj). However, in some cases, the desire is no to have inheritance, and for it to return undefined. Currently, it would require doing this:

Object.getOwnPropertyDescriptor(obj, something).value

which will return undefined if the property is not obj2's own property.

Syntax?

Maybe some syntax could enable this?

own obj2[something]

or

obj2.%something

?

special non-syntactic symbol?

If syntax is not doable, maybe a special Symbol type could prevent the engine from doing prototype lookup and we could at least do this with the special Symbol, bit not string properties? F.e.:

const something = Symbol.own()

// ...same as before...

obj2[something] // does not do prototype lookup, returns undefined 

This is a somewhat obscure thing to do, which means it doesn't warrant syntax or other magic, and given that constraint I think Object.hasOwn(x, k) ? x[k] : void 0 is sufficient.

1 Like

I don't think it's all that obscure - any use of null objects could instead be a normal object only used with a "get own" semantic, as well as matching well with Object.assign and object spread only using own keys - but I agree there's not currently enough of a compelling case to warrant syntax (and i'm not a fan of anything that isn't simple sugar over that oneliner)

1 Like

Ah, yes. hasOwn. Forgot about that.

Huh, the hasOwn version is the slowest, even slower than getting the own descriptor! Benchmark: WeakMap vs Symbol 2 - MeasureThat.net

A syntax feature would make this the fastest (property lookup being faster than WeakMap.get in the benchmark)

That may be that Object.hasOwn isn't optimized as it's relatively new. lib: use `ObjectHasOwn` instead of alternatives by anonrig ยท Pull Request #54710 ยท nodejs/node ยท GitHub

1 Like

I think there is a typo in the benchmark it has:

Object.hasOwn(sy) instead of Object.hasOwn(to1, sy)

2 Likes

I don't thing I'd want syntax for this either, but at least another function to go with Object.hasOwn() would be nice.

Object.getOwn(obj, key)

1 Like

Oops! Fixed: Benchmark: WeakMap vs Symbol 3 - MeasureThat.net
It is still slowest at the moment.

That could be useful. I suppose then Object.setOwn(...) would skip inheritance too, overriding any prototype accessor with a value descriptor.

Is the expectation to call a setter if its an own setter, and define a value property if no property exists?

@mhofman not in particular. I've been maintaining decorators for some years now, and what I find is that when implementing meta code, I usually don't want inheritance.

For example just last night I adopted the new standard context.metadata to track data for getters/setters, and I needed to block inheritance on metadata to achieve my specific goal:

Otherwise I would accidentally override features for a base class.

In various meta programme cases, I need to skip inheritance like that, so that a subclass feature overrides anything from a base class feature.

Here's another example where I need to ensure subclass has an own prop:

Or here:

(don't worry about the impl, I'm refactoring it yet again now that standard decorators allow some new things hehe)

This (and basically anything with decorators) is meta programming, and that's where I usually run into the need. Maybe not to common for average code where people may only be writing classes and using the decorators.

With some syntax, this would be doable:

foo.%bar // get own
foo.%bar = {} // set own
foo.%bar ??= {} // set own if does not exist 

getOwn/setOwn could be nifty.

const foo = Object.hasOwn(o, 'foo') ? Object.getOwn(o, 'foo') : Object.setOwn(o, 'foo', [])

where setOwn returns the set value (to match semantics of = returning the set value)

But there are bigger fish to fry.

My expectation would be that "get own" and "set own" would do exactly the same thing as it would do if you'd set the [[Prototype]] to null right before doing the operation.

1 Like

Right, so I was actually wrong earlier. setOwn would call an existing own setter if it exists, or define the property if no own accessor property exists. It would fail if a own getter only property existed.

It's all fairly consistent, but possibly too niche for a new standard API. However while getOwn is pretty straightforward in userland, setOwn is harder to get right (highlighted by the mistake I made). That might motivate the existence of such standard helpers?

1 Like