Skip to content
This repository has been archived by the owner on Jan 25, 2022. It is now read-only.

tc39/proposal-accessible-object-hasownproperty

Repository files navigation

Accessible Object.prototype.hasOwnProperty()

Proposal for an Object.hasOwn() method to make Object.prototype.hasOwnProperty() more accessible.

👋 Now Gathering Community Feedback

Please see the Implementations section for polyfills and a codemod to start using Object.hasOwn() in your code today.

If you are using Object.hasOwn() please provide feedback in issue #18 (positive and/or negative feedback is encouraged).

Status

This proposal is currently at Stage 4

Authors:

Slides:

Motivation

Today, it is very common (especially in library code) to write code like:

let hasOwnProperty = Object.prototype.hasOwnProperty

if (hasOwnProperty.call(object, "foo")) {
  console.log("has property foo")
}

This proposal simplifies that code to:

if (Object.hasOwn(object, "foo")) {
  console.log("has property foo")
}

There are a number of existing libraries which make this more convenient:

This is a common practices because methods on Object.prototype can sometimes be unavailable or redefined.

Object.create(null)

Object.create(null) will create an object that does not inherit from Object.prototype, making those methods inaccessible.

Object.create(null).hasOwnProperty("foo")
// Uncaught TypeError: Object.create(...).hasOwnProperty is not a function

Redefining hasOwnProperty

If you do not directly own every property defined of an object, you can't be 100% certain that calling .hasOwnProperty() is calling the built-in method:

let object = {
  hasOwnProperty() {
    throw new Error("gotcha!")
  }
}

object.hasOwnProperty("foo")
// Uncaught Error: gotcha!

ESLint no-prototype-builtins

ESLint has a built-in rule for banning use of prototype builtins like hasOwnProperty.

From the ESLint documentation for no-prototype-builtins:


Examples of incorrect code for this rule:

/*eslint no-prototype-builtins: "error"*/
var hasBarProperty = foo.hasOwnProperty("bar");
...

Examples of correct code for this rule:

/*eslint no-prototype-builtins: "error"*/
var hasBarProperty = Object.prototype.hasOwnProperty.call(foo, "bar");
...

MDN hasOwnProperty() advice

The MDN documentation for Object.prototype.hasOwnProperty includes advice not to use it off of the prototype chain directly:

JavaScript does not protect the property name hasOwnProperty; thus, if the possibility exists that an object might have a property with this name, it is necessary to use an external hasOwnProperty to get correct results [...]

Proposal

This proposal adds a Object.hasOwn(object, property) method with the same behavior as calling hasOwnProperty.call(object, property)

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

Implementations

Native implementations of Object.hasOwn in JavaScript engines are available in:

Polyfills of Object.hasOwn() are available in:

A codemod to migrate to Object.hasOwn() from similar libraries is available:

There's also an eslint rule for enforcing usage of hasOwn instead of hasOwnProperty:

Q&A

Why not Object.hasOwnProperty(object, property)?

Object.hasOwnProperty(property) already exists today because Object itself inherits from Object.prototype so defining a new method with a different signature would be a breaking change.

Why the name hasOwn?

See Issue #3

Why not use Map for dictionaries instead of objects?

Excerpt from https://v8.dev/features/object-fromentries#objects-vs.-maps

JavaScript also supports Maps, which are often a more suitable data structure than regular objects. So in code that you have full control over, you might be using maps instead of objects. However, as a developer, you do not always get to choose the representation. Sometimes the data you’re operating on comes from an external API or from some library function that gives you an object instead of a map.

Why not place this method on Reflect?

The purpose of Reflect is to contain, 1:1, a method for each Proxy trap. There is already a method on Proxy that traps hasOwnProperty (getOwnPropertyDescriptor) so it doesn't make sense to add an additional trap, therefore it doesn't make sense to place this method on Reflect.

Related