Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why was Object.keys changed to coerce primitives into objects in ES2015?

Does anyone know the reasoning behind the change from throwing an error when passing a primitive to Object.keys to silently coercing the primitive to an object and returning the result?

I'm not sure that anyone would expect Object.keys('abc') to return [0, 1, 2], and it seems to violate the prime directive of "don't break the web". What if some website has code wrapping a call to Object.keys in a try/catch to handle callers mistakenly passing a primitive?

This is why I feel there must be a strong rationale behind the change. I would be very interested if anyone out there has some information about this.

like image 819
philraj Avatar asked Jun 04 '19 18:06

philraj


1 Answers

I couldn't find any mention of this decision on esdiscuss, so I can only offer my own perspective.

As a commentor points out, this is part of larger trend in ES 2015 to allow non-object inputs more broadly. In the ES 2015 spec, the phrase, "In the previous edition, a non-object argument always causes a TypeError to be thrown," appears in reference to 10 different methods on Object.

For one, this change brings the behavior of Object.keys into congruity with the behavior of for-in loops, which have always been able to operate on primitives. Considering that the specification already required order to match between Object.keys and for-in, requiring the same set of valid operands seems unsurprising.

This change seems nearly as harmless as can be to existing code while greatly reducing the brittleness of Object.keys. Even in your case of a try-catch, it's difficult to imagine a case where successful Object.keys execution causes an actual problem. I can easily imagine code like this:

try {
    var keys = Object.keys(input);
} catch {
    // oops, input was a primitive; call `new [Constructor]` to wrap it
    var keys = Object.keys(
        new input.constructor(input)
    );
}

But this doesn't break when Object.keys doesn't error out; a successful Object.keys call makes the catch code obsolete.

Of course there might exist, somewhere, code like this:

try {
    var keys = Object.keys(input);
} catch {
    // oops, input was a primitive; that unlocks the secret prize
    giveUserAFreePuppy();
}

Basically, what I'm trying to say, through a very silly example, is that the cases where skipping a catch block would be truly problematic for the operation of some code seem so far-fetched that breaking such code seems a small price to pay to get a less brittle Object.keys function.

like image 137
apsillers Avatar answered Sep 18 '22 09:09

apsillers