12

I frequently get an array of an objects keys using:

Object.keys(someobject)

I'm comfortable doing this. I understand that Object is the Object constructor function, and keys() is a method of it, and that keys() will return a list of keys on whatever object is given as the first parameter. My question is not how to get the keys of an object - please do not reply with non-answers explaining this.

My question is, why isn't there a more predictable keys() or getKeys() method, or keys instance variable available on Object.prototype, so I can have:

someobject.keys()

or as an instance variable:

someobject.keys

And return the array of keys?

Again, my intention is to understand the design of Javascript, and what purpose the somewhat unintuitive mechanism of fetching keys serves. I don't need help getting keys.

3 Answers 3

18

I suppose they don't want too many properties on Object.prototype since your own properties could shadow them.

The more they include, the greater the chance for conflict.


It would be very clumsy to get the keys for this object if keys was on the prototype...

var myObj: {
   keys: ["j498fhfhdl89", "1084jnmzbhgi84", "jf03jbbop021gd"]
};

var keys = Object.prototype.keys.call(myObj);

An example of how introducing potentially shadowed properties can break code.

There seems to be some confusion as to why it's a big deal to add new properties to Object.prototype.

It's not at all difficult to conceive of a bit of code in existence that looks like this...

if (someObject.keys) {
    someObject.keys.push("new value")
else
    someObject.keys = ["initial value"]

Clearly this code would break if you add a keys function to Object.prototype. The fact that someObject.keys would now be a shadowing property breaks the code that is written to assume that it is not a shadowing property.


Hindsight is 20/20

If you're wondering why keys wasn't part of the original language, so that people would at least be accustomed to coding around it... well I guess they didn't find it necessary, or simply didn't think of it.

There are many possible methods and syntax features that aren't included in the language. That's why we have revisions to the specification, in order to add new features. For example, Array.prototype.forEach is a late addition. But they could add it to Array.prototype, because it doesn't break proper uses of Array.

It's not a realistic expectation that a language should include every possible feature in its 1.0 release.

Since Object.keys does nothing more than return an Array of an Object's enumerable own properties, it's a non-essential addition, that could be achieved with existing language features. It should be no surprise that it wasn't present earlier.


Conclusion

Adding keys to Object.prototype most certainly would break legacy code.

In a tremendously popular language like JavaScript, backward compatibility is most certainly going to be an important consideration. Adding new properties to Object.prototype at this point could prove to be disastrous.

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

26 Comments

+1. Yes, I imagine there was/is a lot of JS code out there similar to the object in your example. It would be pretty annoying if a new feature in the language forced changes in legacy code.
@nailer: As I've stated, Object.keys wasn't part of the language specification until ECMAScript 5. At that point, it was considered too late to reserve them, because, as I stated above "they can't easily create new reserved words since it'll break old code that uses those words."
Note that the methods on Object aren't actually reserved words. It is not a syntax error to say Object.keys = function() {alert("I broke it")}. And you can do the same for methods on the prototype: x = { hasOwnProperty : function(){return false;}); and Object.prototype.hasOwnProperty = function () { alert("Hi"); } both work.
@nailer: Object.keys is not a mechanism of enumeration. JavaScript has enumeration syntax, called for-in. "... completely ignores the motivation behind these situations" The motivation for what? To not reserve words they haven't yet thought of as relevant to the language? Reserved words limit the vocabulary a developer has available to express code. Is that what you're asking? This has gone far beyond your original question.
@nailer: You really need to read more closely. I said it's not "a mechanism of enumeration". JavaScript already has a mechanism of enumerating keys. It's called for-in. That syntax enumerates the (enumerable) keys of an Object. I've answered your question. The unintuitive mechanism of collecting keys (which is what Object.keys does) stems from the fact that shadowed properties cause issues, so they don't want to do that. It's very simple.
|
8

I guess an answer to your question is "Because the committee decided so", but I can already hear you ask "why?" before the end of this sentence.

I read about this recently but I can't find the source right now. What it boiled down to was that in many cases you had to use Object.prototype.keys.call(myobject) anyway, because the likelihood of myobject.keys already being used in the object for something else.

I think you will find this archived mail thread interesting, where for example Brendan Eich discuss some aspects of the new methods in ECMAScript 5.

Update: While digging in the mail-archive I found this:

Topic: Should Object.keys be repositioned as Object.prototype.keys

Discussion: Allen argued that this isn't really a meta layer operation as it is intended for use in application layer code as an alternative to for..in for getting a list of enumerable property names. As a application layer method it belongs on Object.prototype rather than on the Object constructor. There was general agreement in principle, but it was pragmatically argued by Doug and Mark that it is too likely that a user defined object would define its own property named "keys" which would shadow Object.prototype.keys making it inaccessible for use on such objects.

Action: Leave it as Object.keys.

Comments

4

Feel free to make your own, but the more properties you add to the Object prototype, the higher chance you'll collide. These collisions will most likely break any third party javascript library, and any code that relies on a for...in loop.

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

for (var key in {}) {
    // now i'm getting 'keys' in here, wtf?
}

var something = {
    keys: 'foo'
};

something.keys(); // error

13 Comments

This code breaks jQuery and many others, and you didn't mention it.
@Esailija i thought the collision warning covered that, clarified.
It cannot be emulated, you simply specify it when creating the property: Object.defineProperty( Object.prototype, "keys", {value: function(){}, enumerable: false}) But this is a different issue from collission, which cannot be solved.
@nailer: The language could have included it on Object.prototype, and made it non enumerable, just like it does with the other properties. That was not a barrier at all (assuming language design is the consideration).
A lot of misunderstanding here. Collision refers to a simple shadowing inconvenience, which is the reason stuff is put on the constructor. The enumerability is a completely different problem and can easily be solved, as is done for the other methods in Object.prototype. This answer is false (As in it's not the collision that make other libraries not work but the enumerability) and missing the point.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.