Primitives and Object Wrapper Equivalence in JavaScript - javascript

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.

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.

Related

SonarQube suggest a!==a instead of a===NaN

There is a SonarQube JavaScript Rule (javascript:S2688) which says that the use of a === NaN is a bug because it's always false.
I agree with that but I think to use a !== a instead (this is suggested by SonarQube) is a very bad idea.
It's a funny JavaScript fact but certainly not a "best practice".
What about Number.isNaN(a)? Why is this not the suggested solution? Are there any differences or problems which I've missed?
the use of a === NaN is a bug because it's always false.
This behaviour is not a bug, because it is how NaN has been defined to work. But if you actually used a === NaN in a program then that would be a bug because of the always-false result.
a !== a instead ... is a very bad idea. It's a funny JavaScript fact but certainly not a "best practice".
I disagree with your "certainly". Due to problems with the original global isNaN() function (which I'll explain in a moment), a !== a was, historically the best way to test for NaN. So in fact it is a very common practice to use that technique, and I would expect the vast majority of experienced JavaScript developers to be familiar with it.
NaN is the only value that tests as not equal to itself.
What about isNaN(a)? Why is this not the suggested solution? Are there any differences or problems which I've missed?
The original, global isNaN() function doesn't actually test whether its argument is NaN. Nor does it test if its argument is some other non-numeric value. What it does is first try to convert its argument to a number and then test if the result of that conversion is equal to NaN. This implicit conversion means that, e.g., isNaN("test") returns true even though a string is not equal to the value NaN. And isNaN("") returns false because an empty string can be coerced to 0. If that behaviour is what you're looking for then yes, use isNaN().
So all of that is why ECMAScript 6/2015 introduced a new function, Number.isNaN(), which does test specifically for the value NaN, giving an equivalent result to the old-school a !== a.
As suggested in the comments, for older browsers (basically old IE) that don't support Number.isNaN(), if you want something clearer than a !== a and don't mind longer code you can do this:
typeof a == "number" && isNaN(a)
...which is one of the two Number.isNaN() polyfills suggested by MDN. (The other just uses a !== a.)

Using a function or typeof

How much slower faster is the typeof operator than a function call? Or is it negligible and micro-optimising?
if (isNumber(myVar)) {
}
if (typeof myVar === 'number') {
}
Or is it negligible and micro-optimising?
Yes, this is definitely something to worry about if and only if you identify the code in question as being a performance bottleneck, which is really unlikely. It's micro-optimization. Function calls are really, really fast even if they don't get optimized out by the JavaScript engine. I used to worry about function call overhead when Array#forEach first appeared on the scene. Even back then, it wasn't an issue, even on the oldest, slowest JavaScript interpreter I could find: The one in IE6. Details on my blog: foreach and runtime cost
Re whether it takes longer... How long is a piece of string? It totally depends on the JavaScript engine you're using and whether the code in question is identified as a "hot" spot by the engine (assuming it's an engine like V8 that works in stages and optimizes hot spots).
A modern engine is likely to inline that if it becomes important to do so. That is not a guarantee.
Or is it negligible and micro-optimising?
It's negligible and micro-optimizing.
If you want to check if something's a number, I recommend using an isNaN check and then casting to a number.
if (!isNaN(myVar)) {
myVar = +myVar;
}
In this way, you don't actually care how the value gets treated as a number.
Someone using the API could then choose to pass an object that can be treated as a number:
myVar = {
valueOf: function () {
return 5;
}
};

Do any standard JavaScript APIs or libraries return strings as String objects?

Until recently, I didn't realise that there are two types of strings (and bools, and numbers) in JavaScript: primitives such as "blah", and objects such as new String("blah").
They differ in very "gotcha"-prone ways, the biggest one of which seems to be the different typeof value ("string" vs "object"), but a number of other differences exist, some documented at MDN.
There's no point in creating String objects because the primitive strings work just as well, and JSHint even treats this as an error. So I'd really like to pretend String instances don't even exist, and only support primitive strings in my code.
This makes me wonder: can I get a surprise String instance by calling some standard API that returns a string? Or is it really safe to assume that unless someone screws up by explicitly writing new String, I will never see one of these?
This github search shows that some people are using new String.
And this answer has a nice explanation for String obj:
No native JavaScript object, major library or DOM method that returns
a string will return a String object rather than a string value.
However, if you want to be absolutely sure you have a string value
rather than a String object, you can convert it as follows:
var str = new String("foo");
str = "" + str;
I am not aware of any convention to only use primitive strings. There is also an ever growing amount of API's available, and I'm pretty sure every now and then someone will pull the constructor trick. I would like to give some debugging advice on how to spot this quicky, but I think the typeof check is the only obvious way to quicky note the difference.
You could use a check like this answer, although I think it is not worth the trouble:
function isString(string){
return typeof myVar == 'string' || myVar instanceof String;
};
isString("foo"); //returns true
isString(new String("bar")); //returns true
isString({}); //returns false;
Conclusion: I'm afraid you just have to pray that you don't encounter
these quirky corner cases.

What does undefined === undefined compare?

After reading through Lodash's code a bit and learning that it uses typeof comparisons more often than not (in its suite of _.is* tools), I ran some tests to confirm that it is faster, which it is, in fact (if marginally).
Discussing my confusion with a fellow developer, he pointed out that in case 1:
var a;
return a === undefined;
Two objects undergo comparison, whereas in case 2 (the faster case):
var a;
return typeof a === 'undefined';
is a far more simple and flat string compare.
I was always of the thought that undefined inhabited a static place in memory, and all triple equals was doing was comparing that reference. Who's correct (if either)?
JSPerf Test:
http://jsperf.com/testing-for-undefined-lodash
In this case (with var a in scope), both of the two posted pieces of code have identical semantics as x === undefined is only true when x is undefined and typeof x will only returned "undefined" when x is undefined (or not resolved in the execution context).
That out of the way, we end up with:
undefined === undefined
vs.
"undefined" === "undefined"
So, even in a naive implementation case the "difference" between the two is SameObject(a,b) vs StringEquals(a,b) as per the rules for Strict Equality Comparison.
But modern implementations of JavaScript are quite competitive and, as can be seen, are very well optimized for this case. I don't know of the exact implementation details, but there are at least two different techniques that could allow the performance (in FF/Chrome/Webkit) to be the same for both cases:
ECMAScript implementations may intern strings, such that there is only one "undefined" string. This would make the StringEquals(a,b) effectively the same as SameObject(a,b) which would end up amounting to a "pointer comparison" in the implementation - this alone could explain why the performance is the same.
Additionally, because typeof x === "undefined" is a common idiom, it could be translated during the parsing to end up in a special call that performs the same, say TypeOfUndefined(x). That is, the implementation could bypass the === (and StringEquals) entirely if it so chose.
IE comes out as a the "clear loser" and seems to be missing an applicable optimization here.
The implementation of the undefined value is outside the scope of ECMAScript; there are no mandates that it must be a single value/object in the implementation.
In practice, however, undefined (and other special values like null) is most likely implemented as either a singleton (e.g. "one object in memory") or as an immediate value or value flag (such that there is actually no undefined object).
According to EcmaScript (see http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf ) if both expressions are strings (typeof returns a string) it perfroms a string comparison (char by char), otherwise it just compares references (in case when one of the sides is undefined). There is no way that string comparison would be more efficient then simple two numbers (i.e. addresses in memory) comparison. However it is possible that it is highly optimized (browsers do not follow EcmaScript in 100%) so it's really hard to say.
Proper benchmark:
http://jsperf.com/undefined-comparison-reference-vs-string
Note that in your test the initial value of a is undefined (may or may not matter, it seems that it does).
Conclusion: always use
return a === undefined;
It definitely won't be slower and it might be faster. Besides it seems more natural to compare something to a special undefined object rather then to a string.

How reliable are the polyfill/shim implementations on MDN

I have been looking through the polyfill implementations on the Mozilla Developer Network (MDN) as I require a few of these for a library. I know shim.js exists, but I'm not using that.
It seems that the polyfills are not consistent in code styling. It almost appears that they are written by the community in an almost "wiki" style.
Take for example String.prototype.contains
if(!('contains' in String.prototype)) {
String.prototype.contains = function(str, startIndex) {
return -1 !== String.prototype.indexOf.call(this, str, startIndex);
}
}
it seems more logical to me to implement this as such:
if(!String.prototype.contains) {
String.prototype.contains = function(str, startIndex) {
return this.indexOf(str, startIndex) !== -1;
}
}
Given that JavaScript is a size critical language (in that everything should be as small as possible for network transmission), my example should be favourable to the example on MDN as this saves a few bytes.
As the title suggests, I want to know how reliable the code is on MDN, and should I modify this as necessary to provide really clean, tiny implementations where possible?
It seems that your question refers to the article on String.contains().
Yes, MDN is a wiki so the quality of its content (including code examples) can vary. However, the content on general web topics (as opposed to extension development for example) is usually pretty good. Still, you shouldn't forget to use common sense.
The polyfill suggested on MDN and your version differ in three points:
!('contains' in String.prototype) vs. !String.prototype.contains to check whether a property exists: The former is clearly preferable. The in operator merely looks up a property, there are no side-effects. !String.prototype.contains on the other hand will actually retrieve the value of that property and convert it to a boolean value. Not only is this marginally slower, some property values like 0 will be wrongly coerced to false. You probably won't notice the difference with functions but this might become a real issue when polyfilling other property types.
-1 !== foo vs. foo !== -1 for comparisons: This is a matter of taste but some people prefer the former variant. The advantage of always putting the constant first in comparisons is that you won't unintentionally turn a comparison into an assignment: writing -1 = foo when you meant -1 == foo will cause an error. On the other hand, foo = -1 instead of foo == -1 will succeed and noticing that issue in your code might take a while. Obviously, if you choose to adapt that style you need to use it consistently throughout all your code.
String.prototype.indexOf.call vs. this.indexOf: The former guards against the situation that the indexOf method on the this object is overwritten. As a result, it is closer to the behavior of the native String.contains() function. Consider this example:
var a = "foo";
a.indexOf = function() {something_weird};
alert(a.contains("f"));
The native implementation of String.contains and a polyfill using String.prototype.indexOf.call will work even if this.indexOf is overwritten - a polyfill using this.indexOf however will fail.
Altogether, the code provided on MDN has a few more fail-safes. Whether these are required in your individual scenario is not given of course. However, dropping them to save a few bytes is the wrong approach to optimization ("premature optimization is the root of all evil"). Personally, I prefer good style over efficiency unless the difference in performance is known to be relevant.

Categories

Resources