Why are falsy values not equal to each other in JavaScript? - javascript

A falsy value is a value that translates to false when evaluated in a Boolean context.
https://developer.mozilla.org/en-US/docs/Glossary/Falsy
Exactly the following values are falsy in Javascript: false, 0, "", null, undefined, and NaN.
Now I am seeing this weird behavior: only the falsy values false, 0, and "" are equal to each other, but not equal to the other falsy values.
In addition null and undefined are not equal to any falsy values other than themselves.
And last but not least, NaN isn't equal to any falsy value, not even to itself!
Yet all of them evaluate to false in a Boolean expression like if (value) { ... }
What's the benefit or use case of this seemingly inconsistent behavior?
Note: By "equal" I mean (lax) equality defined by the == operator, rather than strict idendity defined by the === operator.

Why are falsy values not equal to each other in JavaScript?
Because there's a difference between two values that coerce to the same value when used as a boolean, and those two values being equal to one another.
What's the benefit or use case of this seemingly inconsistent behavior?
There we get into largely subjective territory, which isn't really on-topic for SO. Let's just say that JavaScript's loose equality rules have long been the subject of debate and confusion. One has to remember that they were created by Brendan Eich during those mad 10 days in May 1995 in which he created JavaScript, it's no great surprise there are some "clangers" in there. And remember his task was to create a tolerant, forgiving language for non-programmers to program in. Hence automatic semicolon conversion, loose equality, and so on. This was a language that was meant to let you grab a value from an input and treat it like a number, even though the value of an input is always a string. Whether these rules achieved that goal is a matter of opinion (and I'm not advancing one here either way :-) ).
Most of the "weirdness" comes about as a by-product of rules that mostly seem reasonable (with, again, a couple of clangers). Let's consider the thing about "" == 0 being true:
The rule for == is the abstract equality comparison algorithm, which says that if the first operand (x) is a string and the second operand (y) is a number, we return the result of ToNumber(x) == y. Leaving aside the question of whether a coercing equality operator is a good idea or not, if you're going to have one, that seems like a reasonable way to approach string == number. So we look to the ToNumber algorithm, which says that ToNumber(string) interprets the string as a number using the ToNumber Applied to the String Type algorithm, which says amongst other things that an empty string becomes 0.
So the question isn't "why is "" equal to 0", it's "why does "" coerce to the value 0 when treated as a number?" Only Eich can answer that question. :-)
There are some other fun ones in the main ToNumber algorithm, such as that undefined => NaN but null => 0. Again, only Eich can explain why he thought null should equate to 0 but undefined equate to NaN.
It's probably worth noting that he probably didn't just pick this stuff at random. He'd studied several other languages and made choices based on that knowledge. Some of those choices may look very odd to us in retrospect (implicit globals? really?), but then, most people have more than 10 days to create a language, time in which they can seek input from colleagues and the community. And again, there was that "tolerant" design criterion.
I may have strayed into some subjectiveness there. Apologies if so; I tried not to. :-)
Final note: If you find the rules arbitrary and hard to memorize, you aren't alone. One way to handle that is to ensure you do all your type coercion explicitly. But note that even if you try to do that, you're stuck with loose comparisons, because although there's a strict equality operator (===) and a strict inequality operator (!==), there are no strict versions of >, <, >=, or <=. So if you miss out an explicit coercion, your code may still seem to work if you're doing any of those other relations. I'm not advocating this (nor am I advocating against it), I've seen bugs caused both by explicit type coercion and by not having explicit type coercion. Just saying that it's one approach to the tricky rules.

It's probably related to preference. == null is very useful to determine if something is in a "not set" state, as it also checks for undefined; but does NOT return true for values like "", or 0; which will often come back from a form as valid values.
My understanding is that NaN fails all comparison checks so that it is noticed as soon as possible, so as to mean "This is not the value you were expecting." This works out if someone's type-checking block ends in an else { throw exception because I don't know what this is! }

I see no benefit to comparing false with 0, "", null, NaN, or undefined. There are no use cases, in my mind, that justify its use. If you write your code properly, this odd behavior is factored out. The reason the behavior exists in the first place is because JavaScript is loosely typed. Everything is an object. Best practice is to avoid use of the == equality operator and instead use ===.
Fundamentally, you shouldn't allow variables to be coerced into different types across the use of a program. It hinders understandability, readability, and maintainability. It's better to code Boolean operations explicitly (i.e. do this if (myVar === undefined) {...}, not if (!myVar) {...}).

To some extent the inconsistency is probably down to "Wat?!?" - see https://www.youtube.com/watch?v=FqhZZNUyVFM.
I mean, it's kind of odd that false, 0, and "" are considered equal, given they're different kinds of thing (well, I think it's odd anyway)... but I suppose this is why JS has both a strict and a lax equality operator.
And it's why tools like jshint and jslint encourage you to only use the strict === operator, so you don't fall foul of this kind of weirdness. This then forces you to test for truthiness or falsiness directly, rather than via lax equality.
Or to turn it around, if you think it's odd that falsey values aren't equal to eachother, it would be weird if this weren't the behaviour for truthy values. For example, you'd never expect "Bananas" and "Apples" to be equal.
(And, to more directly answer your question: I'd bet some weird historical reason that might get a mention in JavaScript: The Good Parts. Beyond that I don't know.)

Related

Is it safe to use integers (1, 0) instead of booleans (true, false) to minify JavaScript?

When JavaScript is used client-side, it has to be transferred over the network, which is why we commonly minify the code, for example by removing whitespace and shortening identifiers (variable and function names, etc).
In JavaScript / ECMAScript, many functions will accept a boolean to trigger certain behavior. For example, DOMTokenList.toggle() or EventTarget.addEventListener(). Using the integers 1 and 0 instead of the boolean values true and false, respectively, when calling upon such functions would save some additional bytes.
However, JavaScript has two comparison operators, == and ===, and while 1 == true would evaluate to true, 1 === true would evaluate to false. Then again, maybe the standard dictates that built-in functions have to be able to deal integers when accepting a boolean or similar. In short, is it safe to replace booleans with integers in the context of built-in functions?
It depends on the built-in function. For some, passing an integer produces the same result as passing a boolean, for others it doesn't. In general, yes, if a function expects a boolean it will attempt to coerce any value into a boolean, but there are probably exceptions.
However, a minifier does in general not know whether a function call is a call to a builtin function, or what the function's signature is. The first goal is to be correct (not introducing bugs), not to output the smallest bundle.
A standard trick that minifiers do employ, and which is always equivalent, is replacing false with !1 and true with !0.

Why to use strict comparison in string status checking?

I oftentimes see answers using strict comparison (===) instead of normal comparison (==) on status checking, i.e. here:
if(document.readyState === 'complete') ...
I would understand the reason if it were applied on empty string and the obtained value could be also other falsy value with different meaning. But when applied on non-empty string (like 'complete' in the sample), I believe the result is always the same for '==' and '==='. Is that so?
Some people measured that '===' can be faster, but I haven't seen a real world example where it would make any observable difference, so I don't take this micro-optimalization seriously.
On the other hand, anytime I see this operator, I read it as a warning "mind the type here!". But since document.readyState is always string, it annoys me that the original coder made me to study the code what other types there can appear - only to find out that only string.
To me, it is a strong reason to be polite to those who read my code and never use '===' when the type plays no role in the comparison.
Since '===' appears in similar cases in many SO answers and many expert pages, I would like to know if it is just social bandwagon or if there is any good reason why to use it in status checking.
I would recommend always using '===' instead of '==' when strict equality checking is required, which it is in most cases, for this reason: it declares intent. When I see code with '===', I will read it as 'a must be referentially or primitively equal to b'. When I see '==', I will read it as 'a must be coercibly equal to b'. From that, I will judge what kind of goal the code / original programmer is trying to accomplish and how they are passing data around to get the job done. Essentially, it yields insight into the context of the application, the way data is being passed around, and how this function / method / code block fits into the picture.
With that being said, if I see someone do 'a == b' when they are both strings, I'm not going to get on any high horse and make a fuss about it.
It always depends on what you want to achieve, and what you consider to be equal.
== is not always bad, but it often can lead to false assumptions.
If you have something like this:
class TestA {
toString() {
return 'complete'
}
}
class TestB {
valueOf() {
return 'complete'
}
}
let testA = new TestA()
let testB = new TestB()
console.log(testA == 'complete')
console.log(testB == 'complete')
Then testA == 'complete' might be exactly what you want, but because it does an implicit cast, you might do a false assumption about it being a string when it evaluates to true.
So if you later want to call something like substring on it, or passing it to another function that expects an actual string, then it might fail with an unexpected error.
Using === over == is mostly about maintainability, if you start to refactor code or if you look at older code and you see a == you always need to think about if you really want to have an implicit cast at that point or if it this was an accident. So you need to follow the code flow or check if it was documented.
Using an explicitly and === does not prevent you from doing other mistakes, but it keeps the code consistent.

Why use triple-equal (===) in TypeScript?

In JavaScript, it's commonly seen as best practice to use === instead of ==, for obvious and well-known reasons.
In TypeScript, which is one to be preferred? Is there even one which is preferable to the other one?
IMHO, using === in TypeScript doesn't make sense, since comparison already only works on equal types, hence you won't have the (more or less funny) coercion game as in plain JavaScript. If you take aside compatibility to JavaScript for a minute, TypeScript could even get rid of ===, couldn't it?
short version:
== can do unexpected type conversions, in Javascript 1=="1" is true. The === operator avoids this. Comparing different types with === is always false.
The typescript compiler will emit an error message when you compare different types with ==. This removes the unexpected type conversions that can happen with == in Javascript.
This is a case where valid Javascript leads to an error message in the typescript compiler. The idea that all valid Javascript is also valid Typescript is a common misconception. This is simply not true.
longer version:
I think the accepted answer is misleading. Typescript actually does fix == vs === (as far as possible at least).
In Javascript there are two comparison operators:
== : When comparing primitive values, like numbers and strings, this operator will apply a type conversion before doing the comparison. 1 == "1" evaluates to true.
===: This operator does not do type conversions. If the types don't match it will always return false.
Also, both operators will compare reference types based on their references. Two separate objects are never considered equal to each other, even if they store the same values:
let a = {val:1};
let b = {val:1};
c = a;
a==b; // false
a===b; // false
a==c; //true
a===c; //true
So there you have the two common sources of errors in Javascript comparisons:
comparing different types with == can lead to unexpected type conversions.
comparing objects and arrays is based on references not values stored inside.
As the existing answer already says, Typescript is designed as a superset of Javascript. So it doesn't change the behaviour of these comparison operators. If you write == in Typescript, you get type conversions.
So how is this fixed? With the compiler. If you actually do write code that compares incompatible types with == it's a compiler error. Try compiling the following sample:
let str = "1";
let num = 1;
console.log(str == num);
The compiler will tell you:
comparisons.ts:4:13 - error TS2367: This condition will always return 'false' since the types 'string' and 'number' have no overlap.
4 console.log(str == num);
~~~~~~~~~~
Found 1 error.
It is a common misconception that any valid Javascript is also valid Typescript. This is not true and the code above is an example where the typescript compiler will complain about valid Javascript.
This fixes the first of the two sources of errors: unexpected type conversions. It doesn't deal with the second source of errors: comparisons based on references. As far as I know, when you want to do a comparison based on values stored by the objects, you simply can't use these operators. You'll have to implement your own equals() method.
Also, you may have noticed that the compiler error is wrong. The comparison will not always evaluate to false. I think this is a bug in typescript and have filed an issue.
Imagine you're designing TypeScript from scratch. Essentially, you're trying to optimize for making safer code easier to write (TypeScript design goal 1) with a few caveats which prevent you from doing everything you'd like.
JavaScript compatibility (TypeScript design goal 7)
JavaScript should be valid Typescript with no changes.
CoffeeScript makes no guarantees regarding this, so it can convert all instances of == to === and simply tell users don't rely on =='s behavior. TypeScript cannot redefine == without breaking all JavaScript code that relies on its behavior (despite this having sad implications for 3).
This also implies that TypeScript cannot change the functionality of === to, for example, check the types of both operands at compile time and reject programs comparing variables of different types.
Further, compatibility is not limited to simply JavaScript programs; breaking compatibility also affects JavaScript programmers by breaking their assumptions about the differences between == and ===. See TypeScript non-goal number 7:
Introduce behaviour that is likely to surprise users. Instead have due consideration for patterns adopted by other commonly-used languages.
JavaScript as the target of compilation (TypeScript design goal 4)
All TypeScript must be representable in JavaScript. Further, it should be idiomatic JavaScript where possible.
Really though, the TypeScript compiler could use methods returning booleans for all comparisons, doing away with == and === entirely. This might even be safer for users: define a type-safe equality method on each TypeScript type (rather like C++ operator==, just without overloading).
So there is a workaround (for users comparing classes). unknown or any variables can have their types narrowed before using the type-safe equality method.
Which to prefer
Use === everywhere you would in JavaScript. This has the advantage of avoiding the pitfalls common to ==, and doesn't require you to maintain an additional method. The output of the TypeScript compiler will be close to idiomatic JavaScript. Using == has very much the same pitfalls as JavaScript, particularly when you have any, [], or {} involved. As an exception, using == null to check for null or undefined may save headaches if library code is inconsistent.
A method for reference equality (behavior like === for classes) could be confused with a deep/value recursive equality check. Furthermore, === is widely used in TypeScript, and making your code fall in line with conventions is usually more important than any small bit of type safety.
Your intuition was correct. There's no sense to use === in TypeScript to imitate an equality check. The argument that TS compiles to JS "so you should use what is better in JS" is not valid. Why? Because Typescript ensures that both operands of comparison operator(s) have the same type. When both operands have the same type == and === behave identically. And by "identically" I mean 100% identical not just "alike". So there's no more correct or less correct version when both behave exactly the same way in JavaScript.
I guess other commenters here are just looking for ways to preserve their habit of using === or in other words to rationalize. Unfortunately, pure logic tells otherwise: there's no sense to replace == with ===, unless you're going to modify generated JS code manually which is probably never the case.
I use === exclusively for identity checks (when you compare x to x – the same variables, it's sometimes necessary in library code related to memoization). And my counter of errors related to eqeq operator shows 0.
Example:
const s : string = "s"
const n : number = 1
console.log(s == n)
TS2367: This condition will always return 'false' since the types 'string'
and 'number' have no overlap
My opinion is that one should always use ===.
First line of reasoning: TypeScript does not change == to ===. There're TypeScript translators which just strip types. So using === everywhere leads to more robust code if for some reason you forgot to type check your program or if you (or future maintainer of your code) used type casts to override type safety. Should not happen but many things should not happen.
Second line of reasoning: null == undefined. This is true with TypeScript as well. I think that if one writes if (x == null) it makes code less readable because it implies check for undefined as well and implicit code is less readable than explicit if (x === null || x === undefined). Also subtle bugs might occur if this is not done on purpose.
I don't see any issues when just using === everywhere unconditionally other than aesthetic preferences.

Why would you want to convert an object to boolean when used in a boolean context?

I understand that !! converts anything to a boolean value, but why would you want to do this:
if (!!someObject) { ... }
When you can just do:
if (someObject) { ... }
EDIT:
Just to clarify, I'm simply asking why you would write code like in the first example, rather than that in the second example. Are there any practical differences?
There isn't a significant different between them. So, most likely, it's personal preference or to be explicit about the intent.
Though, it's possibly from an uncertainty/misunderstanding of how or when values are treated as a booleans.
But, both if statements and Logical NOT operators use the internal ToBoolean():
If ToBoolean(GetValue(exprRef)) is true, [...]
Let oldValue be ToBoolean(GetValue(expr))
The operators just add 2 more rounds of ToBoolean() with negation on top of the if statement's own use. But, the result is the same.

Why use === when you are sure types are equal?

It seems that the strict equality operator is preferred whenever possible - I put my code in JSLint and got the following feedback.
Code:
function log() {
console.log(arguments.length == 1 ? arguments[0] : arguments);
}
Feedback from JSLint:
Problem at line 2 character 34: Expected '===' and instead saw '=='.
I am curious to know what advantages === has over == here. Basically, .length returns a Number, and 1 is a Number as well. You can be 100% sure, so === is just a redundant extra token. Also, checking for the type whilst you know the types will always be the same has no performance advantage either.
So what's actually the reason behind using === here?
The only reason is that you don't have to think whether the comparison you are doing will involve coercion or not. If you stick to using ===, you just have one thing less to worry about.
Of course not everyone agrees. This is why you can disable specific checks in JSlint, if you are sure of what you are doing.
It's not strictly required, as you know the length function is well-tested. But what if length was a bit buggy, and there was a chance it could return false?
In this situation, both 0 and false would be evaluated as false using the == comparison. To test for false (when there is also the chance of 0 being returned), you need to also check the data type, which is where === comes in.
Well, === is supposed to be infinitesimally faster, as it can fail faster (on type mismatch), but that's deep inside the "pointless microoptimizations" territory.
Realistically, I'd advise for ===, even if you are 105% sure - I've been tripped up by my assumptions too many times ("yes, I am positive they are the ... same ... uh ..."). Unless there's a good reason to turn them off, it is better to keep sanity checks enabled in code than trust to do them in your mind.

Categories