Object.containsOwnProperty/Object.getOwnProperty

I know some people's gut reaction to this would be to say "Just use a Map", but, sometimes a map isn't the appropriate data structure, like when you're dealing with an object where the keys are already known in advance (e.g. an instance of a class). Take this example:

class User {
  constructor({ name, birthday }) {
    this.name = name
    this.birthday = birthday
  }
}

function getUserAttr(user, attr) {
  return user[attr]
}

const user = new User({ name: 'Sally', birthday: '1/2/1999' })
const userSuppliedKey = 'toString'
console.log(getUserAttr(user, userSuppliedKey)) // Whoops, a bug!

Right now, the recommended way to fix this issue is to redefine getUserAttr() as follows:

function getUserAttr(user, attr) {
  if (Object.prototype.hasOwnProperty.call(user, attr)) {
    return user[attr]
  }
  return undefined
}

Can't we provide some native functionality that's, you know, a little less hacky?

Here's what I propose:

Object.containsOwnProperty(obj, key)

Returns true if the obj parameter contains the key (as an own property). Object.containsOwnProperty(obj, key) would behave exactly the same as Object.prototype.hasOwnProperty.call(obj, key)

Example:

> Object.containsOwnProperty({ x: 2 }, 'x')
true
> Object.containsOwnProperty({ x: 2 }, 'toString')
false

Object.getOwnProperty(obj, key)

If the obj parameter contains the key (as an own property), then the associated value is returned, otherwise, undefined is returned.

Example:

> Object.getOwnProperty({ x: 2 }, 'x')
2
> Object.getOwnProperty({ x: 2 }, 'toString')
undefined

Name bikeshedding

"containsOwnProperty" was the best name I could think of, given "hasOwnProperty" already exists on Object's prototype.

Some other potential name combinations:

Object.has()/Object.get() // Works, but I would really like to have the word "own" in there somewhere.
Object.hasOwn()/Object.getOwn() // That works. It's a little awkward.
Object.isOwnProperty()/Object.getOwnProperty() // Also works

Other suggestions are welcome.

Sounds a lot like (stage 3):

let object = { foo: false }
Object.hasOwn(object, "foo") // true

let object2 = Object.create({ foo: true })
Object.hasOwn(object2, "foo") // false

let object3 = Object.create(null)
Object.hasOwn(object3, "foo") // false
2 Likes

Ah, that's perfect, thanks! I should do a better job at looking through existing proposals first :man_facepalming:

For the particular example you give, I'm also hoping that we eventually make class extends null work. In general I think if you are using an object as a map you should ensure that object has a null prototype, so that normal property access works as expected. That's easy enough with regular objects, using Object.create or { __proto__: null, ... }, but still broken for classes.

2 Likes