2

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

dclowd9901
  • 6,458
  • 9
  • 40
  • 59
  • 2
    Any reason why you can't just try it? – Evan Trimboli Dec 11 '13 at 21:02
  • 1
    `undefined` represents a *value* (of which there is only one, namely `(void 0)`). – user2864740 Dec 11 '13 at 21:03
  • @zerkms it is still possible to redefine undefined `(function(undefined){ return undefined })(2)` I just ran into a bug with an older knockout.sortable caused by this the other day – George Mauer Dec 11 '13 at 21:04
  • http://stackoverflow.com/questions/80646/how-do-the-equality-double-equals-and-identity-triple-equals-comparis?lq=1 – kritya Dec 11 '13 at 21:05
  • @GeorgeMauer That's shadowing the global property `undefined` (`window.undefined = "hate this job"`). I hold such code is outside the scope of sane discussion of what undefined is - although it is an un-fun "gotcha" in borked contexts. – user2864740 Dec 11 '13 at 21:05
  • Why do you think a string comparison would be simpler than comparing two objects (even if these actually are not objects)? – Bergi Dec 11 '13 at 21:05
  • @Bergi, I wouldn't think so, but it is. Objectively. That's why I'm asking this question. – dclowd9901 Dec 11 '13 at 21:07
  • possible duplicate of [How to check for "undefined" in JavaScript?](http://stackoverflow.com/questions/3390396/how-to-check-for-undefined-in-javascript) – Bergi Dec 11 '13 at 21:07
  • "the faster case" -- faster from what perspective? – zerkms Dec 11 '13 at 21:08
  • @dclowd9901 Is the 2nd case *really* faster? Have a micro-benchmark? In this case both the `==` and `===` should involve the approximate same number of steps (as there is no [[ToString]] required in the `==`) .. – user2864740 Dec 11 '13 at 21:08
  • 1
    This questions seems to me to be asking two different things - 1) How does javascript view `undefined`, as a point in memory or something more? 2) Why can an `===` of a string be faster than a check of an actual variable reference. You might have more luck breaking it up into those two questions and asking explicitly – George Mauer Dec 11 '13 at 21:09
  • 1
    Dude, you really ought not to claim "faster" for a 0.49% difference. They have the *same* performance in the given micro-benchmark, which is not surprising since the `==` does *not* need to do a [[ToString]] here. – user2864740 Dec 11 '13 at 21:11
  • 3
    @user2864740 it's still interesting as to *why* there is a difference. – George Mauer Dec 11 '13 at 21:12
  • ..And why the difference falls to what would seem to be the least performant comparison. – dclowd9901 Dec 11 '13 at 21:13
  • 1
    @dclowd9901 This is not an interesting comparison between `==` and `===` as **there is no "difference" in the given micro-benchmark** (except in IE, but I think we can chalk that up to an implementation failing due to the other results). – user2864740 Dec 11 '13 at 21:13
  • 1
    @GeorgeMauer What difference? The JSperf OP added shows the exact same values (per browser of course) for every browser tested thus far (3 at this time). 0.2% difference is noise. *EDIT: IE was just added and - as usual - is the exception. So what, it's IE...* – Mörre Dec 11 '13 at 21:16
  • @Mörre I'm not so sure that it does. The graph shows a no difference, sure, for v8, but that doesn't mean that there's no difference, just not a visible one. When I'm running it in my environment on chrome I DO indeed see a consistant slight preference each time for the typeof comparison. Obviously it's so light to be pedantic, but pedantic doesn't mean useless. Does anyone know a way to get raw numbers for jsperf runs? – George Mauer Dec 11 '13 at 21:24
  • @GeorgeMauer Why don't you do your own test runs? Just make the loop long enough and you can even measure very accurately using just a stop watch. And don't come back with 0.##% differences - that's just measurement noise! You are not solving a math equation, you measure in the real world :) – Mörre Dec 11 '13 at 21:25
  • @Travis J: that's weird. One day I told that it's to prevent modifying `undefined` and got an evidence that now it's not possible, hmmmm – zerkms Dec 11 '13 at 21:25
  • Second case is slower for me. http://i.imgur.com/S42xumS.png – Travis J Dec 11 '13 at 21:26
  • 2
    @TravisJ Did any of you guys not have to run experiments in school or university? That is NOT a difference that you can attribute clearly to the experiment itself, that's just noise, typical measurement error! If you want, run your own (single line!) loop (in your browser's JS console), and make it LOOOONG, just make the nr in the `for` loop (or whatever) really big. When it runs at least a 1 minute and you still get only 1% difference you did NOT get a difference. When it exceeds 10% we're getting something. – Mörre Dec 11 '13 at 21:29

2 Answers2

3

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.

freakish
  • 48,318
  • 8
  • 114
  • 154
  • 1
    @freakish I haven't read the ecmascript standard myself, but normally in these sorts of documents there are provisions for optimization that allow arbitrary structural changes which do not affect semantics. As long as a program behaves as if it is comparing character by character, then it is irrelevant how it is actually doing it. – Tim Seguine Dec 11 '13 at 21:38
  • @Tim I haven't read the entire standard as well, so I don't know to be honest. That's why I'm saying "it is possible". I don't know for sure and this is as much as I can say about the issue. :) – freakish Dec 11 '13 at 21:41
  • @Tim: that's indeed the case -- so long as semantics are adhered to, an implementation can do whatever it likes (I once answered a question referring to the specification as to what the implementation does -- ended up getting downvoted to oblivion). – Qantas 94 Heavy Dec 12 '13 at 00:35
3

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:

  1. 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.

  2. 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).

Community
  • 1
  • 1
user2864740
  • 54,112
  • 10
  • 112
  • 187
  • This still doesn't answer the question as to what is being compared. Maybe someone who's read V8 or Gecko should weigh in. – dclowd9901 Dec 11 '13 at 21:54
  • 2
    @dclowd9901 If you are curious about a specific implementation, then you should write your question to ask about it (including a tag for the specific implementation) - browsing the applicable source code repositories would also be a good use of time. – user2864740 Dec 12 '13 at 00:19