JScript Equality Operators, plus More On Mad Crushes

JScript Equality Operators, plus More On Mad Crushes

  • Comments 27

First, the technical stuff.  Here's a recent question I received about missing data in JScript. 

I’ve seen it stated that in order to determine if a variable is undefined, you have to use if("undefined" == typeof(x))
I attended a presentation on JScript today where the presenter used if(undefined == x)
When I asked about that, he stated that it works and he is not aware of any problems with it. Is he correct?

I discussed related issues earlier (here and here and here), but I figure that this is another opportunity for clarification.

There are three statements made. 

1) In order to determine if a variable is undefined, you have to use if("undefined" == typeof(x))
2)  if(undefined == x) works
3) Mr. Presenter Guy is not aware of any problems with (2).

The first statement is demonstrably false because of that "have to" in there.  The typeof trick works, but "have to" implies incorrectly that it is the only thing that works.  This works just as well: if (x === undefined) (Note that triple equals in there.  More on that below.)

The second statement is also demonstrably false, because it has "false positives".  Yes, this detects when a value is undefined, but…

var x = null;
print(x == undefined);

will evaluate as true, even though x is clearly defined!  undefined and null compare as equal to each other; this does not determine if a variable is undefined, it determines if a variable is undefined or is defined as a value which the comparison operator considers as equal to undefined.

The third statement was true; Mr. Presenter Guy was probably not aware of any problems.  I asked the original questioner to point out to Mr. Presenter Guy that the == operator gives false positives, so hopefully the third statement is also now false.

What the heck is up with the === operator?  Here's the thing: the == operator is very lenient when comparing data of different types. 

print("1" == 1);  // true

 Which is often very useful.  But then one day (April 23rd, 1997, if you care) we added the switch statement to JScript and this question immediate arose: what the heck does this do?

x = 1;
switch(x)
{
case "1": print("string"); break;
case 1: print("number"); break;
}

This switch statement in JScript is NOT logically equivalent to

if (x == "1") print("string");
else if (x == 1) print("number")

because then this would print "string" when clearly the other case is by far the better match. 

The switch statement requires that not only must the case and the argument compare as equal, but they must also be of the same type. 

Given that, it would then seem really weird if you could construct a switch statement with these comparison semantics but could not construct the equivalent if statement.  Therefore we added the === operator, which has the same semantics as the comparator used in the switch statement.

This now explains why this works for undefined variables. The === operator checks to see whether the arguments are of the same type; since there is only one member of the undefined type, there can be no false positives.

****************************

In other news, I've managed to avoid the terrible fate that befalls boys who will not propose marriage.  No one will be exiling me to a desert island surrounded by sharp rocks and dropping hate-mail valentine cards from helicopters!  My erstwhile lovely girlfriend Leah is now my charming fiancée Leah, and I couldn't be more pleased.  The plan so far is to get married next summer on the beaver-shark-infested shores of romantic Lake Huron, spend a week or so in romantic Holland, and then throw a huge yet elegant dance party back in romantic Seattle.

(UPDATE: Leah has just sent me an email saying that if anyone does exile me to a desert island, she will rescue me and then bake me that cake. That's good to know.)

  • Got it. Because I can think of three reasons why a method might not be there:

    1) Because you're maybe using some browser that doesn't support it. In that case I'd recommend checking for browser version explicitly, etc.

    2) Because the property might accidentally be reset. Then a simple check would suffice.

    3) Because the property might be deliberately reset by hostile code that you didn't write -- like a script injection attack. In that case you have a very serious problem on your hands that would need careful threat modelling before we could begin to mitigate it.

  • Ok, so let's say malware.com has owned your document host object. What do they need your script for? They can inject or modify any content they want. It seems like they already have all the keys at this point.
  • Exactly! That's why I wanted to make sure that the question wasn't actually a security question. Determining whether any function actually does what you think it does is pretty much impossible if the environment is hostile, and it is pointless to try.
  • Congrats on the engagement! May you two "live happily ever after!"
  • A hearty congratulations, Eric. Wish you a long, prosperous and blissful married life ahead.
  • === is well known to any PHP programmer worth their salt. On the other hand, 6 years after inital implementation comments and questions about checking for type (or far more commonly, warnings about 'mysterious' failures when checking a return value for false when it can be int/string or false on failure, and the ugly workarounds for it) still regularly pop up, even from fairly knowledgeable people who never felt the need to learn how their language of choice differs from C/C++/Java. This and other background differences is probably one of the major pitfalls of giving an untyped language a familiar C family typed syntax.
  • I think that the "undefined" thing has a more subtle behavior. Compare the following two programs.

    Program #1:
    var foo;
    if (foo === undefined) var bar = 1;

    Program #2:
    if (foo === undefined) var bar = 1;

    Program #1 will run fine, resulting with bar==1. But program #2 will crash (try it!), because foo is undeclared when the if statement is reached. The *only* safe way to avoid this is doing:
    if (typeof(foo) == "undefined") var bar = 1;

    Program #2 may also be made to run by putting somewhere in it a declaration of foo:
    var foo;

    This way, when the if statement is reached, foo is already declared, and is equal to undefined.

    BTW, declaring foo does not change its value, so you can safely do it even if you're not sure whether foo was previously declared. The only concern here is that declaring it inside some scope (such as a function), will make it a local variable, overriding a variable with the same name from a surrounding scope.

    BTW, there is a nice idiom that uses this technique to add a default value in case foo is undefined:

    if (foo === undefined) var foo = 3;

    The if statement will not cause a crash, because the parser collects all var declarations before executing the script, so foo is already declared when the if is reached.
  • Excellent point, Roy. I hadn't considered the fact that typeof does not activate the "undeclared variable" logic.
  • I've talked a few times in this blog about the semantics of the equality operators in various languages....
  • I think that the code

    if(x === undefined)

    is actually testing that the variable named "x" is strictly equal to the variable named "undefined". undefined is not a keyword, so a declaration such as

    var undefined = true;

    is perfectly legal. This declaration would obviously break the strict equality test, so I think that

    if(typeof(x) == "undefined")

    is really the only "correct" way to do this test.

    Sorry to post a comment so out of date, but I just recently discovered your blog and have been enjoying reading through your old posts.

  • You are correct, and I should have called that out in the post.

  • PingBack from http://script.shabunc.org/?p=35

Page 2 of 2 (27 items) 12