Why does ",,," == Array(4) in Javascript? - javascript

Boot up your interpreter/console and try the comparison
> ",,," == Array(4)
True
Why? At first I thought maybe since you could think of ",,," as an array of four characters with a '\0' terminating slice, that might be why, but
> "..." == Array(4)
Returns "False". So... why? I know it's some idiosyncratic bit of duck typing in Javascript, but just curious what underlines this behavior. Gleaned this from Zed Shaw's excellent presentation here btw.

Because the right hand operand is converted to a string and the string representation of Array(4) is ,,,:
> Array(4).toString()
",,,"
If you use the array constructor function and pass a number, it sets the length of the array to that number. So you can say you have four empty indexes (same as [,,,]) and the default string representation of arrays is a comma-separated list of its elements:
> ['a','b','c'].toString()
"a,b,c"
How the comparison works is described in section 11.9.3 of the specification. There you will see (x == y):
8. If Type(x) is either String or Number and Type(y) is Object,
return the result of the comparison x == ToPrimitive(y).
(arrays are objects in JavaScript)
and if you follow the ToPrimitive method you will eventually find that it it calls toString.

Internally its going
",,," == Array(4).toString()

Try using ===. When using == in Javascript, it will attempt to cast the variables, thus leading to issues like this one. The console is casting Array(4) to the string representation (i.e. Array(4).toString), which is ",,,". The reason the commas are there is that the .toString() function adds them to separate items in an array.
See the snippet below:
document.write( Array(4).toString() );

This is because Array(4) initialises an array of 4 empty values, an == implicitly converts, so:
",,," == Array(4)
",,," == Array(4).toString()
",,," == ["", "", "", ""] // note 3 commas for 4 values
",,," == ["", "", "", ""].toString()
Are all similar.
== does implicit type conversions before comparing the values, which can result in unpredictable results. Use === to check the type and the value.

Comparing an Array to a string coerces the Array to a string before doing the comparison. Coercing an empty 4-element Array to a string yields that exact string.

I first thought it was something with the "prototype"... but after a little investigation I reached a sad conclusion...
Apparently it is an internal and more obscure js thing with not much logic...
Just try
Array(4)==Array(4)
and no coercion on types also...
Array(4)===Array(4)
and you'll get FALSE
you know that null==null, null===null and even undefined==undefined and undefined===undefined returns TRUE... so... it's a bit obscure...
Array(4)==[,,,] should be true also

Related

Comparison String to String

I got these 2 examples below -
console.log("a" > "3") // outputs true
console.log("hello" > "3") // outputs true
According to MDN, If both values are strings, they are compared as strings, based on the values of the Unicode code points they contain.
But then they also wrote the following in the next paragraph, Strings are converted based on the values they contain, and are converted as NaN if they do not contain numeric values.
Following this logic, shouldn't both statements be false since no matter what the operator it is, "a" and "hello" are words in strings and they don't have a numerical value, therefore, it should return NaN, and NaN is false; hence, as soon as one of the operands is false, it outputs false?
If I need to adhere to the former statement above, could you please walk me through this logic?
Thanks.
The key phrase from the MDN article is
If both values are strings, […].
Otherwise JavaScript attempts to convert non-numeric types to numeric values
So no, "a", "3" and "hello" are all strings, and when comparing them, they get compared as strings. No conversion to anything occurs.
You are comparing two strings (in quotation marks), not a string to a number. This should answer your question:
"a" > "3" //true
"a" > 3 //false
As you've written,
If both values are strings, they are compared as strings, based on the
values of the Unicode code points they contain
So, on both examples, both values are strings. If you change "3" to 3, the result is:
console.log("a" > 3)
// expected output: false
console.log("hello" > 3)
// expected output: false
That's because, first it's converted as NaN, and then:
If either value is NaN, the operator returns false.
The docs:
If both values are strings, they are compared as strings, based on the values of the Unicode code points they contain.
Otherwise JavaScript attempts to convert non-numeric types to numeric values: ....
Reading the docs, I understand that, if BOTH strings have numerical values (both not NaN), then both are treated as Unicode code points. Otherwise, if BOTH have numerical values, then they're converted.
But anyways, my sugestion for a js beginer: do not get too attached to how the language works for now, and even tho it can compare strings with "greater" and "less than", I dont think there would (or should) be a real life scenario where you would need it.
Also, just for fun, check this pen I made when I first started with js (I was also very confused about comparison and typing xD):
"" == null // this one is fun
Later, after learning more about the language, you'll get why somethings work like they do in js, I also recomending understanding the history of js and the web, it explains a lot of the weird behaviours of the language.

Using operators as variables gives odd results

! + [] === true
Can someone explain what's happening here? I understand this is not normal, but I'm struggling to understand what the compiler is doing when it seems operators used in place of variables in mathematical operations.
more examples of this strangeness:
(! + '') === (!'')
but
(! + []) !== (![])
JavaScript always convert inputs to Primitive types. So in your case of ! + [] it will try to convert +[] to string. which will be "" and then because of ! it will convert into boolean. in JavaScript "" is consider as false. So ultimately it will return !false which will be true.
Edit 1
As #mhodges has commented below. [] getting convert to string as "" then + will convert it to number, so it will become 0.
You can learn more about object to primitive from here.
object to primitive
You can check behavior below.
var emptyBrackets = [].toString();
// conversion to string.
console.log(emptyBrackets === '');
// conversion to number.
console.log(+emptyBrackets);
// conversion to boolean.
console.log(!0);
First, it is important to know what operators are being used, and in what order. When this is not clear, an easy way to find out is to look at what the syntax parser makes out of it, e.g. using https://astexplorer.net/, which shows for the first example that:
! + [] === true uses unary logical not (!), unary plus (+), an array literal which is empty ([]), strict equality (===) and the keyword for true (true).
Execution order is (!(+[])) === true, or seen as a tree:
===
/ \
! true
|
+
|
[]
What follows is to understand, what each of the operators are transforming their operands to. This is a bit tedious, when you want to know in detail.
I assume you know what an empty array literal or true produce. Strict equality === is likely also known, as it does not have very many unintuitive aspects (contrary to loose equality ==, which has a LOT). Relevant are therefore only the steps of the unary + and unary logical not.
Given the order of operations, we first look at unary + with [] as operand. It performs ToNumber on the operand, which, for objects like our example [], performs ToPrimitive followed by another ToNumber. ToPrimitive will try valueOf (which will not return a primitive and is therefore not taken), followed by toString. The latter, for arrays, searches for the function join and calls it without arguments (setting the array as this). For an empty array, this results in the empty string "", which will be converted to 0 by the following second call of ToNumber.
Next is the unary logical not, which first transforms any operand with ToBoolean. This intermediary step results in false for our operand 0. Then, as expected, the opposite is being returned (true).
Last but least, as we know, true === true evaluates to true.
An interesting sidenote here is the use of Array.prototype.join in the default case. I would expect many to be surprised, why changes to join would affect the outcome:
console.log(! + [] === true);
let x = [];
x.join = () => "1";
console.log(! + x === true);
Array.prototype.join = () => "1";
console.log(! + [] === true);
The other examples work in a similar way:
(! + '') is almost the same, just that ToNumber does not even need the prior ToPrimitive
For (!''), ToBoolean of an empty string is false, therefore resulting in true
For (![]), ToBoolean of any object is true, resulting in false

What is happening in this loose equality comparison of 2 empty arrays

I am struggling with understanding how this snippet works on a basic level
if([] == ![]){
console.log("this evaluates to true");
}
Please help me understand where I got it wrong. My thinking:
First there is operator precedence so ! evaluates before ==.
Next ToPrimitive is called and [] converts to empty string.
! operator notices that it needs to convert "" into boolean so it takes that value and makes it into false then negates into true.
== prefers to compare numbers so in my thinking true makes 1 and [] is converted into "" and then 0
Why does it work then? Where did I get it wrong?
Why does it work then?
TLDR:
[] == ![]
//toBoolean [1]
[] == !true
[] == false
//loose equality round one [2]
//toPrimitive([]); toNumber(false) [3]
"" == 0
//loose equality round two
//toNumber("") [4]
0 === 0
true
Some explanations:
1)
First there is operator precedence so ! evaluates before ==
Negating something calls the internal toBoolean method onto that "something" first. In this case this is an Object (as Arrays are Objects) and for that it always returns true which is then negated.
2)
Now it's up to loose equalities special behaviour ( see Taurus answer for more info) :
If A is an Object ( Arrays are Objects ) and B is a Boolean it will do:
ToPrimitive(A) == ToNumber(B)
3)
toPrimitive([])
ToPrimitive(A) attempts to convert its Object argument to a primitive value, by attempting to invoke varying sequences of A.toString and A.valueOf methods on A.
Converting an Array to its primitive is done by calling toString ( as they don't have a valueOf method) which is basically join(",").
toNumber(false)
The result is 1 if the argument is true. The result is +0 if the argument is false. Reference
So false is converted to +0
4)
toNumber("")
A StringNumericLiteral that is empty or contains only white space is converted to +0.
So finally "" is converted to +0
Where did I get it wrong?
At step 1. Negating something does not call toPrimitive but toBoolean ...
The accepted answer is not correct (it is now, though), see this example:
if([5] == true) {
console.log("hello");
}
If everything is indeed processed as the accepted answer states, then [5] == true should have evaluated to true as the array [5] will be converted to its string counterpart ("5"), and the string "5" is truthy (Boolean("5") === true is true), so true == true must be true.
But this is clearly not the case because the conditional does not evaluate to true.
So, what's actually happening is:
1. ![] will convert its operand to a boolean and then flip that boolean value, every object is truthy, so ![] is going to evaluate to false.
At this point, the comparison becomes [] == false
2. What gets into play then is these 2 rules, clearly stated in #6 in the specs for the Abstract Equality Comparison algorithm:
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)
At this point, the comparison becomes [] == 0.
3. Then, it is this rule:
If Type(x) is Object and Type(y) is either String or Number,
return the result of the comparison ToPrimitive(x) == y.
As #Jonas_W stated, an array's ToPrimitive will call its toString, which will return a comma-separated list of its contents (I am oversimplifying).
At this point, the comparison becomes "" == 0.
4. And finally (well, almost), this rule:
If Type(x) is String and Type(y) is Number,
return the result of the comparison ToNumber(x) == y.
An empty string converted to a number is 0 (Number("") == 0 is true).
At this point, the comparison becomes 0 == 0.
5. At last, this rule will apply:
If Type(x) is the same as Type(y), then
.........
If Type(x) is Number, then
.........
If x is the same Number value as y, return true.
And, this is why the comparison evaluates to true. You can also apply these rules to my first example to see why it doesn't evaluate to true.
All the rules I quoted above are clearly stated here in the specs.
First, realize that you are comparing two different types of values.
When you use ![] it results in false. You have code (at core it results in this):
if([] == false)
Now the second part: since both values are of different type and you are using "==", javascript converts both value type into string (to make sure that they have same type). It starts from left.
On the left we have []. So javascript applies the toString function on [], which results in false. Try console.log([].toString())
You should get false on the left as string value. On the right we have boolean value false, and javascript does the same thing with it.
It use the toString function on false as well, which results in string value false.
Try console.log(false.toString())
This involves two core concepts: how "==" works and associativity of "==" operator - whether it starts from left or right.
Associativity refers to what operator function gets called in: left-to-right or right-to-left.
Operators like == or ! or + are functions that use prefix notation!
The above if statement will result in false if you use "===".
I hope that helps.

Why does ",,," == new Array(4) [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Why does “,,,” == Array(4) in Javascript?
In JavaScript why does
",,," == new Array(4)
It returns true in Chrome Developer Tools, and nodejs console.
console.log(new Array(4).toString()); // ",,,"
casted to string with above value making both equal.
",,," == ",,," // true
JS sees that on left hand is a string and on right hand side an array which is not good for comparison, it casts array to string and then does the comparison.
Notice that:
log(",,," === new Array(4));
would result in false since === checks not only for value but also type and types are different of course.
Because the new Array(4) is being implicitly cast to a string, which will equal ",,," (four empty elements, comma separated).
Because Array(4).toString() returns ",,," - 4 empty elements, so only the commas between them
An array in String form produces a comma separated list of the elements, ie 1,2,3,4. If there are no elements in the Array, it will show up as ,,,.
(new Array(4)).toString() produces ,,,.
Note that new Array(4) === ",,," returns false.

How is "20" and 20 considered equal in JavaScript?

I understand that using the "===" compares type, so running the following code results in "not equal" because it's comparing a number type to a string type.
var a = 20;
var b = "20";
if (a === b) {
alert("They are equal");
} else {
alert("They are not equal");
}
But I dont understand how using the "==" to compare only the value results in the "They are equal" message.
var a = 20;
var b = "20";
if (a == b) {
alert("They are equal");
} else {
alert("They are not equal");
}
How are the values equal? Isn't the string "20" stored as the ASCII characters 50 and 48 (0110010 and 0110000 in binary) and 20 stored as the actual binary number 0010100?
EDIT: Thanks everyone! I think all the responses are great and have helped me understand this much better.
The == operator compares only the values of the variables. If the types are different, a conversion is operated. So the number 20 is converted to the string "20" and the result is compared.
The === operator compares not only the values, but also the types, so no cast is operated. In this case "20" !== 20
The JavaScript engine sees the a as a number and casts the b to number before the valuation.
When type conversion is needed, JavaScript converts String, Number, Boolean, or Object operands as follows.
When comparing a number and a string, the string is converted to a number value. JavaScript attempts to convert the string numeric literal to a Number type value. First, a mathematical value is derived from the string numeric literal. Next, this value is rounded to nearest Number type value.
If one of the operands is Boolean, the Boolean operand is converted to 1 if it is true and +0 if it is false.
If an object is compared with a number or string, JavaScript attempts to return the default value for the object. Operators attempt to convert the object to a primitive value, a String or Number value, using the valueOf and toString methods of the objects. If this attempt to convert the object fails, a runtime error is generated.
The problem with the == comparison is that JavaScript version 1.2 doesn't perform type conversion, whereas versions 1.1 and 1.3 onwards do.
The === comparison has been available since version 1.3, and is the best way to check of two variables match.
If you need your code to be compatible with version 1.1, 1.2 and 1.3 versions of JavaScript code, you should ensure that the variables all match as if it was an === comparison that was being performed.
Part of the definition of "==" is that the values will be converted to the same types before comparison, when possible. This is true of many loosely typed languages.
Javascript is designed such that a string containing numbers is considered "equal" to that number. The reason for that is simplicity of use for the case of users entering a number into an input field and the site validates it in JS -- you don't have to cast the entered string to a number before comparing.
It simplifies a common use case, and the === operator still allows you to compare with the type considered as well.
As far as I know JavaScript does automatic data type conversion on the fly - so maybe the variables are casted to equivalent types automatically.

Categories