36

Today when I was doing some experiments with ==, I accidentally found out that "\n\t\r" == 0. How on earth does "\n\t\r" equal to 0, or false?

What I did is:

var txt = "\n";  //new line
txt == 0;        //it gives me true

And that really annoy me. So I did more:

var txt = "\r";  //"return"
txt == 0;        //true

var txt = "\t";  //"tab"
txt == 0;        //true

It does not make sense, at all. How's that happen? And more crazy is this:

//Checking for variable declared or not

var txt ="\n\t\r";
if(txt!=false){
    console.log("Variable is declared.");
}else{
    console.log("Variable is not declared.");
}

What it gives me is Variable is not declared.

How is it equal to 0, or false???

Derek 朕會功夫
  • 92,235
  • 44
  • 185
  • 247
  • 33
    [Welcome to Javascript](http://wiki.theory.org/YourLanguageSucks#JavaScript_sucks_because:). – user541686 Apr 29 '12 at 21:36
  • 2
    I would say "because the string is empty" – JustSid Apr 29 '12 at 21:37
  • 1
    @Mehrdad - LOL, it said in JavaScript `0.1 + 0.2 --> 0.30000000...4` And I don't believe it and I tried and I got the same thing. Never notice it before! – Derek 朕會功夫 Apr 29 '12 at 21:42
  • @Derek: lol, like I said... welcome to Javascript. :P (Though to be honest, that particular problem is the least worrisome, since it's the same issue in many other languages...) – user541686 Apr 29 '12 at 21:44
  • 8
    @Derek: This is some behavior you will see in every language using IEEE floating point numbers (which is about every language out there) – Holger Just Apr 29 '12 at 21:45
  • 4
    Also this: https://www.destroyallsoftware.com/talks/wat – Holger Just Apr 29 '12 at 21:48
  • @Mehrdad: Just read through most of the wiki page. And I have to say that more than half of it is either plain wrong, describes a deliberate design aspect, is easily avoidable by using best practices, or was an issue 5 years ago (except the PHP stuff, that language just sucks :). But generally, I'm not impressed... – Holger Just Apr 29 '12 at 22:15
  • They are all expected results. Section 11.9.3 explains them all. Standard ECMA-262: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf – Ray Cheng Apr 29 '12 at 22:28
  • @RayCheng: Wouldn't it be odd if the specification for a language would disagree with its implementation? Nevertheless, some concepts like weak typing (JS, PHP) can be unexpected for people used to strongly typed languages (C, Java, Ruby). – Holger Just Apr 29 '12 at 22:36
  • @HolgerJust: Yeah I agree, I wasn't really endorsing it either. I just found it to be a nice link w.r.t. the question. – user541686 Apr 29 '12 at 22:44

5 Answers5

42

This behaviour might be surprising but can be explained by having a look at the specification.

We have to look at the what happens when a comparison with the equals operator is performed. The exact algorithm is defined in section 11.9.3.

I built a simple tool to demonstrate which algorithm steps are executed: https://felix-kling.de/js-loose-comparison/


string == integer

The step we have to look at is #5:

5. If Type(x) is String and Type(y) is Number,
return the result of the comparison ToNumber(x) == y.

That means the string "\n" ("\r", "\t") is converted to a number first and then compared against 0.

How is a string converted to a number? This is explained in section 9.3.1. In short, we have:

The MV (mathematical value) of StringNumericLiteral ::: StrWhiteSpace is 0.

where StrWhiteSpace is defined as

StrWhiteSpace :::
    StrWhiteSpaceChar StrWhiteSpace_opt

StrWhiteSpaceChar :::
    WhiteSpace
    LineTerminator

This just means that the numerical value of strings containing white space characters and/or a line terminator is 0.
Which characters are considered as white space characters is defined in section 7.3.


string == boolean

The step we have to look at is #7:

7. If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).

How booleans are converted to numbers is pretty simple: true becomes 1 and false becomes 0.

Afterwards we are comparing a string against a number, which is explained above.


As others have mentioned, strict comparison (===) can be used to avoid this "problem". Actually you should only be using the normal comparison if you know what you are doing and want this behaviour.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
12

Because JavaScript is a loosely typed language, it attempts to type cast your 1st side of the comparison to the other so that they would match each other.

Any string which does not contain a number, becomes 0 when compared to an integer, and becomes true (Except in certain situations), when compared to a Boolean.

Light reading material.

Madara's Ghost
  • 172,118
  • 50
  • 264
  • 308
  • 11
    It's not a bug, it's a feature! – user541686 Apr 29 '12 at 21:39
  • 2
    Your answer is a bit confusing - if a string is compared to a string it sure remains a string (maybe you wanted to say "number" there?). – Niko Apr 29 '12 at 21:58
  • 2
    Regarding *"Any string which does not contain a number, becomes 0 when compared to an integer"*, how do you explain `"abc" == 0 // false` then? – Felix Kling Apr 29 '12 at 23:39
  • 2
    Also the second part of the statement is not correct: *"and becomes true (if not an empty string), when compared to a Boolean"*. `"0" == false` yields `true`. According to your explanation, `"0"` would be converted to `true`, so it should yield `false`. In fact, both operands are converted to numbers first and are then compared. Note that there is a difference between *comparing* a string to a boolean and *evaluating* a string *as* boolean. – Felix Kling Apr 30 '12 at 00:25
  • Indeed @FelixKling, should probably mention that `"string"==0` basically gets interpreted as `Number("string")==0`. And for some reason whitespace strings get cast to 0, and strings with characters get cast to `NaN`. – TarVK Dec 29 '20 at 14:32
4

txt is not a Boolean, so it will never be false. It can be undefined though.

var txt ="\n\t\r";
if(txt !== undefined) { //or just: if (txt)
    console.log("Variable is declared.");
} else {
    console.log("Variable is not declared.");
}
//=> will log: 'Variable is declared.'

By the way, a declared variable may be undefined (e.g. var txt;).

If you do a stricter comparison (without type coercion, using ===), you'll see that

var txt = '\n'; txt === 0; //=> false
var txt = '\r'; txt === 0; //=> false
var txt = '\t'; txt === 0; //=> false

See also

KooiInc
  • 119,216
  • 31
  • 141
  • 177
1

The reason is that "\n\t\r" just as " " are treated as empty strings. If you use == it will return true but if you use === it will return false.

If you want to test for existence you should use something like

if(typeof strName !== 'undefined') {
    /*do something with strName*/
} else {
    /*do something without it*/
}
Jérémie Parker
  • 3,184
  • 2
  • 20
  • 33
  • 1
    I don't think these quotes are acceptable by JavaScript as string delimiters. – Madara's Ghost Apr 29 '12 at 21:43
  • You're wrong about that - only "" is an empty string, " " is not. Try `if (" ") alert("hi");` to see it in action. – Niko Apr 29 '12 at 21:53
  • `> " " == 0` returns `true ` using node.js – Jérémie Parker Apr 29 '12 at 22:03
  • That's right, because " " is a string - if you compare that to a number (what 0 is), it will be converted to a number as well before comparing the value - hence the actual comparison will be "0 === 0" and that sure is true. – Niko Apr 29 '12 at 22:06
  • it's not because it's a string, because if you test for `"test" == 0` it will return `false`. " " is an empty string whereas "test" is not. – Jérémie Parker Apr 29 '12 at 22:07
  • Ok, you're right - empty strings convert to 0 while "test" will convert to NaN. – Niko Apr 29 '12 at 22:14
1

Whenever you use the == operator and try to compare a string to a number, the string will first be converted to a number. Thus: alert("\n\r"==0) becomes: alert(Number("\n\r")==0) The Number constructure is kind of interesting. It will first strip whitespace then decide if the number is a not a number or not. If NaN, then the result is "NaN". If the string is empty, then the result is 0.

alert(Number()) alerts 0
alert(Number("")) alerts 0
alert(Number(" \n \r \n \t")) alerts 0
alert(Number("blah")) alerts NaN
alert(Number("0xFF")) alerts 255
alert(Number("1E6")) alerts 1000000

To check if the result is NaN use isNaN()

Thus: alert(isNaN("blah")) alerts true
Thus: alert(isNaN("")) alerts false
Thus: alert(isNaN("\n")) alerts false
Thus: alert(isNaN(" ")) alerts false

however do note that NaN will never equal NaN:

var nan=Number("geh");alert(nan==nan);  alerts false 

Update:

if you want to check if both sides are NaN, then you'd convert both to boolean values first like so:

var nan=Number("geh");alert(!!nan==!!nan); alerts true

or better yet

var nan=Number("geh");
alert(isNaN(nan)&& isNaN(nan));
Thalaivar
  • 23,282
  • 5
  • 60
  • 71