Inconsistency in javascript comparison operator [duplicate] - javascript

This question already has answers here:
Why does ('0' ? 'a' : 'b') behave different than ('0' == true ? 'a' : 'b') [duplicate]
(6 answers)
Closed 8 years ago.
Can anyone explain why here a = [] ? 1 : 2 a will be equal to 1 and here b = [] == true ? 1 : 2 b will be equal to 2

A very similar case is handled in Why does ('0' ? 'a' : 'b') behave different than ('0' == true ? 'a' : 'b').
In short:
When you have a single value that is evaluated in a boolean context, it is passed to ToBoolean to be converted to a boolean value.
If you compare values with == though, the algorithm in section 11.9.3 of the ECMAScript specification takes place. In this algorithm, both operands are converted to either strings or numbers and these values are compared.
More detailed explanation
a = [] ? 1 : 2
The condition is converted to a boolean by calling ToBoolean(GetValue(lref)). Now I assume that for objects, GetValue just returns the object itself and all objects evaluate to true.
Since arrays are just objects, the result of this expression is 1.
b = [] == true ? 1 : 2
As already said, quite some type conversion is going on here.
First, true is converted to a number:
7. If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
false is converted to 0 and true is converted to 1.
So we have
[] == 1
Then step 9 applies:
9. If Type(x) is Object and Type(y) is either String or Number,
return the result of the comparison ToPrimitive(x) == y.
That means [] is converted to a primitive value (string or number). If you follow the ToPrimitive function (and section 8.12.8), you'll find out that arrays are converted to strings, by concatenating their elements with ,. Since an empty array as no elements, the result is an empty string.
So we end up with
"" == 1
Then step 5 applies:
5. If Type(x) is String and Type(y) is Number, return the result of the comparison ToNumber(x) == y.
An empty string is converted to 0, hence 0 == 1 is false and the conditional operator returns 2.

Because an array is an object, and when an object is evaluated in the context of a boolean comparison, it is "truthy", but not exactly true.

In the first example the array is being converted to a Boolean which yields true as it's not null, undefined, "", 0 or false.
In the second example you're getting a type conversion because of the equals operator. From mdn:
If the two operands are not of the same type, JavaScript converts the operands then applies strict comparison. If either operand is a number or a boolean, the operands are converted to numbers if possible.
Tried the following in the console:
Number(true) //gives 1
Number([]) //gives 0
So when converted to numbers these are not equal.

In a = [] ? 1 : 2, it returns 1 if [] is not null or undefined.
In b = [] == true ? 1 : 2, it would return 1 if [] is true, but it's not, so it returns 2.

The first is an assignment and thus has to fall through to true (but DOESN'T equate anything). Equating empty to true will be false.

Because initialising an array with [] returns true and [] == true does not :-)

Related

The result of []==false is true, why? [duplicate]

I have two statements like this. Why do they both evaluate to false?
console.log([] == true)
console.log(![] == true)
If [] == true is false shouldn't ![] == true result to true?
It's the way coercion works.
The first step of coercion is to convert any non primitive types to primitive types, then using a set of rules convert the left, right or both sides to the same type. You can find these rules here.
In your case [] == true, would pass through these 4 steps:
[] == true
[] == 1
"" == 1
0 == 1
Whereas based on operator precedence the ! in ![] == true is executed first so the expression is converted to false == true which is obviously false.
You can try the live demo by Felix Kling to better understand how the sameness operator works.
In more words:
The value ![] is false, because [] is an Object (arrays are objects) and all objects, not including null, are truthy. So any array, even if it is empty will always be a truthy, and the opposite of a truthy is always false. The easiest way to check if a value is a truthy is by using !!.
console.log("![]: " + ![]);
console.log("!![]: " + !![]);
When you are using a loose comparison (using == instead of ===) you are asking the JavaScript engine to first convert the values into the same type and then compare them. So what happens is the values get converted according to a set of rules, once they are the same type, they get compared though the same process as a strict equality check (===). The first change that [] goes through is to get converted to a string, the string version of an empty array is an empty string "", the empty string is then converted to a number, the numeric value of an empty string is 0, since the numeric value of true is 1 and 0 != 1, the final output is false.
console.log("[] == true => `" + ([] == true) + "`");
console.log("String([]) => `" + String([]) + "`");
console.log("Number('') => `" + Number("") + "`");
console.log("Number(true) => `" + Number(true) + "`");
As per the Abstract Equality Comparison Algorithm - http://es5.github.io/#x11.9.3
Types of x and y are checked when x == y is to be checked.
If no rule matches, a false is returned.
For [] == true , rule 7 matches, so a result of [] == ToNumber(true) is returned i.e. false is returned.
Reason you're getting the same result for ![] == true, because ![] returns false, and false == true returns false .
To get opposite result for your second operation, add a precedence (i.e. wrap) to your expression with braces.
console.log(!([] == true)); // returns true
Put ![] in the console. It's false.
So ![] == true is the same as false == true, which is false.
[] != true is true though
None of the answers so far has addressed the main problem. [Edit: This is no longer true. See Nick Zoum's answer.]
The question essentially is this:
[] == true returns false
=> [] is not true
=> [] is false (because something not true must be false)
=> ![] should be true, since negation of false is true. Then why does ![] == true return false?
[] is actually truthy. (So ![] is actually falsy.)
Still, [] == true returns false -- the reason is coercion. It's another of Javascript's gotchas.
When an array is checked for equality explicitly against a boolean value (that is, against true or false), its elements are first converted to strings and then joined together. An empty array therefore becomes "" — therein lies the problem, because an empty string is falsy.
In brief, an empty array is truthy, but it's coerced (when being compared to a boolean value) to an empty string, which is falsy.
Note the following, which comes from https://www.nfriedly.com/techblog/2009/07/advanced-javascript-operators-and-truthy-falsy/
Arrays are particularly weird. If you just test it for truthyness, an
empty array is truthy. HOWEVER, if you compare an empty array to a
boolean, it becomes falsy:
if ( [] == false ) {
// this code runs }
if ( [] ) {
// this code also runs }
if ([] == true) {
// this code doesn't run }
if ( ![] ) {
// this code also doesn't run }
(This is because when you do a comparison, its elements are turned to Strings and joined. Since it's empty, it becomes "" which is then falsy. Yea, it's weird.)
Read https://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/
Rule in JavaScript for The Equals Operator (==)
if
type(x) type(y) result
1.x and y are the same type See Strict Equality (===) Algorithm
2.null Undefined true
3.Undefined null true
4. Number String x == toNumber(y)
5. String Number toNumber(x) == y
6. Boolean (any) toNumber(x) == y
7. (any) Boolean x == toNumber(y)
8.String or Number Object x == toPrimitive(y)
9.Object String or Number toPrimitive(x) == y
***10.otherwise… false

Why do both "[] == true" and "![] == true" evaluate to false?

I have two statements like this. Why do they both evaluate to false?
console.log([] == true)
console.log(![] == true)
If [] == true is false shouldn't ![] == true result to true?
It's the way coercion works.
The first step of coercion is to convert any non primitive types to primitive types, then using a set of rules convert the left, right or both sides to the same type. You can find these rules here.
In your case [] == true, would pass through these 4 steps:
[] == true
[] == 1
"" == 1
0 == 1
Whereas based on operator precedence the ! in ![] == true is executed first so the expression is converted to false == true which is obviously false.
You can try the live demo by Felix Kling to better understand how the sameness operator works.
In more words:
The value ![] is false, because [] is an Object (arrays are objects) and all objects, not including null, are truthy. So any array, even if it is empty will always be a truthy, and the opposite of a truthy is always false. The easiest way to check if a value is a truthy is by using !!.
console.log("![]: " + ![]);
console.log("!![]: " + !![]);
When you are using a loose comparison (using == instead of ===) you are asking the JavaScript engine to first convert the values into the same type and then compare them. So what happens is the values get converted according to a set of rules, once they are the same type, they get compared though the same process as a strict equality check (===). The first change that [] goes through is to get converted to a string, the string version of an empty array is an empty string "", the empty string is then converted to a number, the numeric value of an empty string is 0, since the numeric value of true is 1 and 0 != 1, the final output is false.
console.log("[] == true => `" + ([] == true) + "`");
console.log("String([]) => `" + String([]) + "`");
console.log("Number('') => `" + Number("") + "`");
console.log("Number(true) => `" + Number(true) + "`");
As per the Abstract Equality Comparison Algorithm - http://es5.github.io/#x11.9.3
Types of x and y are checked when x == y is to be checked.
If no rule matches, a false is returned.
For [] == true , rule 7 matches, so a result of [] == ToNumber(true) is returned i.e. false is returned.
Reason you're getting the same result for ![] == true, because ![] returns false, and false == true returns false .
To get opposite result for your second operation, add a precedence (i.e. wrap) to your expression with braces.
console.log(!([] == true)); // returns true
Put ![] in the console. It's false.
So ![] == true is the same as false == true, which is false.
[] != true is true though
None of the answers so far has addressed the main problem. [Edit: This is no longer true. See Nick Zoum's answer.]
The question essentially is this:
[] == true returns false
=> [] is not true
=> [] is false (because something not true must be false)
=> ![] should be true, since negation of false is true. Then why does ![] == true return false?
[] is actually truthy. (So ![] is actually falsy.)
Still, [] == true returns false -- the reason is coercion. It's another of Javascript's gotchas.
When an array is checked for equality explicitly against a boolean value (that is, against true or false), its elements are first converted to strings and then joined together. An empty array therefore becomes "" — therein lies the problem, because an empty string is falsy.
In brief, an empty array is truthy, but it's coerced (when being compared to a boolean value) to an empty string, which is falsy.
Note the following, which comes from https://www.nfriedly.com/techblog/2009/07/advanced-javascript-operators-and-truthy-falsy/
Arrays are particularly weird. If you just test it for truthyness, an
empty array is truthy. HOWEVER, if you compare an empty array to a
boolean, it becomes falsy:
if ( [] == false ) {
// this code runs }
if ( [] ) {
// this code also runs }
if ([] == true) {
// this code doesn't run }
if ( ![] ) {
// this code also doesn't run }
(This is because when you do a comparison, its elements are turned to Strings and joined. Since it's empty, it becomes "" which is then falsy. Yea, it's weird.)
Read https://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/
Rule in JavaScript for The Equals Operator (==)
if
type(x) type(y) result
1.x and y are the same type See Strict Equality (===) Algorithm
2.null Undefined true
3.Undefined null true
4. Number String x == toNumber(y)
5. String Number toNumber(x) == y
6. Boolean (any) toNumber(x) == y
7. (any) Boolean x == toNumber(y)
8.String or Number Object x == toPrimitive(y)
9.Object String or Number toPrimitive(x) == y
***10.otherwise… false

Why (!a) and (a == false) are not equal?

Talk is cheap, I will show my code.
var a; // a = undefined
if(a == false){ // As I typed == not ===, a needs to be translated to boolean (undefined == false) but it doesn't
return false;
}
else {
return true;
}
// true
This returns true but I was sure that it would return false because undefined is the same as false when I'm using double equal.
Things came strange when I tried to use
if(!a){..} else {..};
// false
Here I got my false but till this moment I thought (!a) and (a == false) are absolutely equals.
The short answer:
!a converts a value to a Boolean.
a == false compares a value to a Boolean.
These are two different operations.
!a is equivalent to Boolean(a) ? false : true. Boolean(a) returns false if a is
undefined
null
0
''
NaN
false
In every other case it returns true.
What happens in a == false is a bit more evolved, but not that complicated. The most important thing that happens is that false is converted to a number, so you are actually comparing a == 0. But undefined is treated in a special way in the comparison algorithm. It's not converted to any other type, so the algorithm simply returns false.
I wrote the following interactive tool for a JavaScript course which shows you which steps of the algorithm are performed when comparing two values:
Similar questions:
In JavaScript, why is "0" equal to false, but when tested by 'if' it is not false by itself?
Why does ('0' ? 'a' : 'b') behave different than ('0' == true ? 'a' : 'b')
JavaScript: What is the difference between `if (!x)` and `if (x == null)`?
Why "" == "0" is false in javascript?
The only correct answer is "that's the way it is". The source of your confusion is a feature of JavaScript called type coercion and different types of equality comprisons (==, === in JavaScript).
There's an interesting table which tells you which comparisons will result to true on JavaScript Equality Table.
The only two values which will give true when ==-compared with null are null and undefined.
In other words, x == null will be true if and only if x is null or undefined.
You had a false assumption. x == false does not coerect x to boolean. In fact, == has it's own equality table.
If you don't believe random blogposts, here is the spec:
7.2.12 Abstract Equality Comparison
The comparison x == y, where x and y are values, produces true or
false. Such a comparison is performed as follows:
ReturnIfAbrupt(x).
ReturnIfAbrupt(y).
If Type(x) is the same as Type(y), then Return the result of performing Strict Equality Comparison x === y.
If x is null and y is undefined, return true.
If x is undefined and y is null, return true.
If Type(x) is Number and Type(y) is String, return the result of the comparison x == ToNumber(y).
If Type(x) is String and Type(y) is Number, return the result of the comparison ToNumber(x) == y.
If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
If Type(x) is either String, Number, or Symbol and Type(y) is Object, then return the result of the comparison x == ToPrimitive(y).
If Type(x) is Object and Type(y) is either String, Number, or Symbol, then return the result of the comparison ToPrimitive(x) == y.
Return false.
So for undefined == false: first we hit #9, and then #12, which evaluates to false.
(!a) and (a == false) are absolutely equals.
you use two different operators and assume they are absolutely equals - never do this, there is a reason 2 different operators exists instead of 1.
Now, think of NaN (as one example, others may apply). By definition NaN is a falsy value but not false value, so:
if(!NaN) {} // this will execute
if(NaN == false) {} // this will not execute
Why do you think this happens?
Because == operator does type coercion per type/value, thus NaN doesn't coerce to false, while others, like 0 may, but both will be considered falsy and converted to true using !
Summed:
operator ! uses a defined set of falsy values (false,0, '' or "" (empty string), null, undefined, NaN) all else is truthy
operator == uses per type conversions not regarding if the original value is considered falsy, so after being converted, it may well be truthy

JavaScript transitive equality

Learning JavaScript and found that given the expression below, it evaluates to true when given this: transitive([1], 1, {toString:_=>'1'});
I don't understand why.
it makes sense that the y and z are equal but how can the x and y be equal if the x and z are not equal?
function transitive(x,y,z) {
return x && x == y && y == z && x != z;
}
In JavaScript there is a concept of "truthiness" and "falsyness". Values like 1 and '1' are equal when compared with a loose comparison operator like ==, but are not strictly equal using a strict equality operator like ===.
The object is equal to 1 because JavaScript uses type coercion to convert the object to a comparable value to a primitive. It does this by calling the .toString() method of the object, which returns '1' and as we learned above is truthy, as is 1, so they are considered equal when using ==.
This will probably be relevant: MDN: Equality comparisons and sameness.
It is best/common practice in JavaScript to always use === and !== in place of == and !=.
The "loose" equality operator in javascript (==) is not transitive. This is primarily a result of type coercion that occurs before comparison. == uses javascript's "abstract equality" algorithm which roughly boils down to these steps. Successive coercions can occur.
If one operand is a primitive and another is an object (including an array), a not so simple algorithm is used to coerce the object to a primitive, which can involve (among other things) the object's toString, valueOf, or [Symbol.toPrimitive] method.
Booleans are coerced to numbers.
If one operand is a number and the other is a string, the string is coerced to a number.
null and undefined are considered equal to themselves and eachother and nothing else. Otherwise a strict equality comparison is performed.
That's a lot of complexity packed into a single operator and the result is a very wiggly concept of equality with a bunch of gotchas.
// Is an empty array truthy or falsey?
[] == false; // -> true, so [] is falsey?
[] ? true : false; // -> true, so [] is truthy?
// Another case of intransitivity:
false == "0" // -> true
false == [] // -> true
"0" == [] // -> false
// Not super consistent
123 == "123" // -> true
123 == [123] // -> true
true == "true" // -> false
true == [true] // -> false
"true" == [true] // -> true
true == ["1"] // -> true
Applying these coercion rules to your example:
[1] == 1
// Object is coerced to primitive, in this case via toString
"1" == 1
// String is coerced to number
1 == 1
/* true */
1 == {toString:_=>'1'}
// Object is coerced to primitive, in this case via toString
1 == "1"
// String is coerced to number
1 == 1
/* true */
[1] == {toString:_=>'1'}
// Since neither value is a primitive, no coercion occurs.
// Reference equality is checked instead.
/* false */
Just about the only reason I use == is to check if a value is either null or undefined with a single comparison. Otherwise I stick with === which is much easier to reason about.

why does "[] == 0" return true while "[]" is true and "0" is false?

If i execute the following lines in thw browser console
!![] //--- returns true
!!0 //--- returns false
I get that [] and 0 have different boolean values.
I don't understand why
[] == 0 //--- returns true
returns true.
What am I missing?
Remember that array is object and 0 is number.
And as "user2864740" told..
1) When you doing
!![] //--- returns true
!!0 //--- returns false
You are performing so called "ToBoolean" convertion
https://es5.github.io/#x9.2
Number
The result is false if the argument is +0, −0, or NaN; otherwise the
result is true.
Object ( our [] )
always true
2) But when you using == you performing so called "Equality Comparison"
https://es5.github.io/#x11.9.3
Here thins a little bit complicated but to understand what happens you have to remember that == do a type coercion ( so you can compare oranges to apples :) )
First of all compiler converts [] to some primitive type.
If Type(x) is either String or Number and Type(y) is Object, return
the result of the comparison x == ToPrimitive(y).
How ToPrimitive works is a matter of an article :), but's it easy to remember that closet primitive type to array is string. Array will be converted to empty string.
[].toString() === ""
So now we need to compare empty string and number 0
"" == 0 // true
Hmmm. So it's true. But why is that? Remember that when you compare with "Equality Comparison" number and string
If Type(x) is Number and Type(y) is String, return the result of the comparison x == ToNumber(y).
So let's try to convert empty string to number
Number("") === 0
And in the end
0 === 0
I hope that's explains something :)
JavaScript is probably converting the array to a number:
!!Number([]) // false
Number([]) == 0 // true

Categories

Resources