Greg
Greg

Reputation: 111

Why is the Object.keys() method not added to Object.prototype?

I am confused about why JavaScript works in a certain way.

If I have an Object set to the variable of obj, and if I wanted to list all the keys in the object, I would say

Object.keys(obj)

Why wouldn't it be the following?

obj.keys()

If I was working with an Array it would be arr.pop(). So why not the same syntax for an obj.keys()? Again, why does it have to be Object.keys(obj)?

Upvotes: 10

Views: 2525

Answers (2)

Patrick Roberts
Patrick Roberts

Reputation: 51886

In addition to the possibility of the hypothetical keys() method being shadowed, there's also the possibility that the value is not an instance of Object at all.

Consider the following example:

// properly define your suggested method
Object.defineProperty(Object.prototype, 'keys', {
  configurable: true,
  writable: true,
  value: function keys () { return Object.keys(this); }
})

const obj = { foo: 'bar', hello: 'world' };

console.log(obj instanceof Object);

// okay
console.log(obj.keys());

const value = Object.create(
  null,
  Object.getOwnPropertyDescriptors(obj)
);

console.log(value instanceof Object);

// existing approach still works
console.log(Object.keys(value));

// suggested approach fails
console.log(value.keys());

Upvotes: 3

CertainPerformance
CertainPerformance

Reputation: 370759

It's possible to put a keys method on Object.prototype so that things work as you're expecting, but it's not a good idea.

Object.prototype.keys = function() {
  return Object.keys(this);
};

const obj = {
  prop: 'val'
};
const keys = obj.keys();
console.log(keys);

With object keys, the issue is that objects can generally have any sort of keys. The object may even have a key named keys. So, for example:

const obj = {
  name: 'building 1',
  keys: 'foo'
}

Here, if you did obj.keys(), you would get a TypeError, because the keys property refers to the property directly on the object, rather than the Object.prototype method.

Object.prototype.keys = function() {
  return Object.keys(this);
};

const obj = {
  name: 'building 1',
  keys: 'foo'
};


const keys = obj.keys();
console.log(keys);

So, the method to use to get all keys of an object was put on Object instead, as a static method, rather than a prototype method on Object.prototype, so as to avoid possible name collisions.

Arrays, on the other hand, are pretty much universally expected to have a certain few methods particular to arrays (like push), and nothing else. Arrays are not generic objects - you'd pretty much always expect an array to have only array methods, and arrays almost never get arbitrary keys added to them. (If you see code that has this, it's probably code that deserves refactoring.) So, with arrays, there's no real possibility of an array method causing name collisions, like there was with a generic object.

Thus, there's no harm in having Array.prototype.push, but Object.prototype.keys could easily cause problems.

Upvotes: 16

Related Questions