Primitives and Object Wrapper Equivalence in JavaScript

444 Views Asked by At

EDIT: Based on everyone's feedback, the original version of this question is more design-related, not standards-related. Making more SO-friendly.


Original:

Should a JS primitive be considered "equivalent" to an object-wrapped version of that primitive according to the ECMA standards?


Revised Question

Is there a universal agreement on how to compare primitive-wrapped objects in current JavaScript?

var n = new Number(1),
    p = 1;
n === p;     // false
typeof n;    // "object"
typeof p;    // "number"
+n === p;    // true, but you need coercion.

EDIT:

As @Pointy commented, ECMA spec (262, S15.1.2.4) describes a Number.isNaN() method that behaves as follows:

Number.isNaN(NaN);                // true
Number.isNaN(new Number(NaN));    // false
Number.isNaN(+(new Number(NaN))); // true, but you need coercion.

Apparently, the justification for this behavior is that isNaN will return true IF the argument coerces to NaN. new Number(NaN) does not directly coerce based on how the native isNaN operates.

It seems that the performance hit and trickiness in type conversion, etc, of directly using native Object Wrappers as opposed to primitives outweighs the semantic benefits for now.

See this JSPerf.

1

There are 1 best solutions below

4
machineghost On

The short answer to your question is no, there is no consensus on how to compare values in JS because the question is too situational; it depends heavily on your particular circumstances.

But, to give some advice/provide a longer answer .... object versions of primitives are evil (in the "they will cause you lots of bugs sense", not in a moral sense), and should be avoided if possible. Therefore, unless you have a compelling reason to handle both, I'd suggest that you do not account for object-wrapped primitives, and simply stick to un-wrapped primitives in your code.

Plus, if you don't account for wrapped primitives it should eliminate any need for you to even have an equals method in the first place.

* Edit *

Just saw your latest comment, and if you need to compare arrays then the built-in == and === won't cut it. Even so, I'd recommend making an arrayEquals method rather than just an equals method, as you'll avoid lots of drama by keeping your function as focused as possible and using the built-in JS comparators as much as possible.

And if you do wrap that in some sort of general function, for convenience:

function equals(left, right) {
    if (left.slice && right.slice) { // lame array check
        return arrayEquals(left, right);
    }
    return left == right;
}

I'd still recommend against handling primitive-wrapped objects, unless by "handle" you make your function throw an error if it's passed a primitive-wrapped object. Again, because these objects will only cause you trouble, you should strive to avoid them as much as possible, and not leave yourself openings to introduce bad code.