What Are the Semantics of Javascripts If Statement - javascript

I always thought that an if statement essentially compared it's argument similar to == true. However the following experiment in Firebug confirmed my worst fears—after writing Javascript for 15 years I still have no clue WTF is going on:
>>> " " == true
false
>>> if(" ") console.log("wtf")
wtf
My worldview is in shambles here. I could run some experiments to learn more, but even then I would be losing sleep for fear of browser quirks. Is this in a spec somewhere? Is it consistent cross-browser? Will I ever master javascript?

"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 either operand is a string, the other one is converted to a string."
https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Operators/Comparison_Operators
So the first one does:
Number(" ")==Number(true)
While the second one is evaluated like this:
if(Boolean(" ")==true) console.log("wtf")

I am guessing that it is the first part that is a problem, not the second.
It probably does some weird casting (most likely, true is cast to a string instead of " " being cast to a boolean value.
What does FireBug return for Boolean(" ") ?

JavaScript can be quirky with things like this. Note that JavaScript has == but also ===. I would have thought that
" " == true
would be true, but
" " === true
would be false. The === operator doesn't do conversions; it checks if the value and the type on both sides of the operator are the same. The == does convert 'truthy' values to true and 'falsy' values to false.
This might be the answer - from JavaScript Comparison Operators (Mozilla documentation):
Equal (==)
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 either operand is a string, the other one is converted to a string
Highly recommended: Douglas Crockford on JavaScript.

Answer: aTruthyValue and true are not the same.
The semantic of the if statement is easy:
if(aTruthyValue) {
doThis
} else {
doThat
}
Now it's just the definition of what a truthy value is. A truthy value is, unfortunately, not something that is simply "== true" or "=== true".
ECMA-262 1.5
Setion 9.2 explains what values are truthy and which are not.

I recommend using === whenever possible, if only to avoid having existential crises.

Related

How to find, what kind of a Javascript error I have?

Quick and a silly question that got me confused.
What kind of error occurs if I use an assignment statement instead of a comparison statement? I need to check if the value of the counter variable is equal to 5.
The code:
if (counter = 5) { /* ==SOME CODE==*/}
VS
if (counter == 5) { /* ==SOME CODE==*/}
The easiest way to pick-up this problem is to reverse the order of the comparison.
Instead of:
if (someVar == 5)
Use:
if (5 == someVar)
This way, if you mess it up, you'll get an assignment error, since you can't assign a different value to the number 5. You can assign a different value to a number object that happens to hold the number 5, but you can't change the 5 itself. ;)
What kind of error occurs if I use an assignment statement instead of a comparison statement?
In your example i assume that a browser would not throw any errors.
I tested https://validatejavascript.com/ with this code:
let counter = 3;
if (counter = 5) {
alert('Will this ever show or always?');
}
A linter might generate a warning - for exmaple
validatejavascript.com threw: 3:5 error Unexpected assignment within an 'if' statement. (no-cond-assign) and
jshint.com threw: Expected a conditional expression and instead saw an assignment.
And based on your comment:
But I need to check if the value of the counter variable is equal to 5.
A simple assignment operator (=) is used to assign a value to a variable.
What you are looking for are operators for Equality comparisons and sameness
JavaScript provides three different value-comparison operations:
=== - Strict Equality Comparison ("strict equality", "identity", "triple equals")
== - Abstract Equality Comparison ("loose equality", "double equals")
Object.is provides SameValue (new in ES2015).
Which operation you choose depends on what sort of comparison you are
looking to perform. Briefly:
double equals (==) will perform a type conversion
when comparing two things, and will handle NaN, -0, and +0
specially to conform to IEEE 754 (so NaN != NaN, and -0 == +0);
triple equals (===) will do the same comparison as double equals
(including the special handling for NaN, -0, and +0)
but without type conversion; if the types differ, false is returned.
Object.is does no type conversion and no special handling for NaN, -0,
and +0 (giving it the same behavior as === except on those special
numeric values).
Note that the distinction between these all have to do with their
handling of primitives; none of them compares whether the parameters
are conceptually similar in structure. For any non-primitive objects x
and y which have the same structure but are distinct objects
themselves, all of the above forms will evaluate to false.

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

Rationale for why JavaScript converts primitive values to numbers in == operator comparisons when one is boolean?

I know the rule:
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; else if either operand is a string, the other operand is converted to a string if possible.
So, if("true") passes but if("true" == true) fails because it is handle like if(NaN == 1).
I was wondering what the rational is behind this when one value is boolean. In other weak typed languages like php, this is handle this differently--if one value is a boolean, the other is converted to a boolean for comparisons (and not covert both to numbers as in javascript).
I'm assuming this choice was made for the == operator on careful consideration. Can anyone provide rational as to why this was the chosen functionality? Is there a common use case that this was chosen to address? I'm betting is wasn't just a mistake.
A remarkably quick response just in from Brendan Eich from the es-discuss#mozilla.org mailing list :
Consider Perl:
$ perl -e 'print 0 == "true";'
1
Ok, poor rationale -- but I created JS in May 1995, in the shadow of AWK, Perl 4, Python 1.2 (IIRC), TCL.
I should have paid more attention to AWK than Perl, given
$ awk 'END {print(0 == "0")}'
1D
$ awk 'END {print(0 == "")}'
0D
In some ways, JS's == operator splits the difference between Perl (where non-numeric strings such as "true" convert to 0) and AWK (where only "0" converts to 0) by converting to NaN. That way, at least, we have
js> 0 == ""
true
js> 0 == "true"
false
But the full truth is not that I was carefully emulating other languages. Rather, some Netscapers working to embed JS (then "Mocha") in a PHP-like server (LiveWire) wanted sloppy conversions, so programmers could match HTTP header strings (server side) or HTML form fields (client side) against, e.g., 404 and the like, without explicit coercion by the programmer.
But it was the 90s, I was in a tearing hurry, these ex-Borland Netscapers were persistent. So, as I said at Strange Loop last year, "I was an idiot! I gave them what they wanted!"
Implicit conversions are my biggest regret in JS's rushed design, bar none. Even including 'with'!
Does anyone know the exact reason the choice was made not to convert to boolean any value compared against a boolean in with the == operator?
The general idea is the narrower type should widen. Thus, true == 1 follows by projecting boolean {false, true} onto {0, 1}, as in C++.
But why not widen true to string, since the other operand in your example is "true"? Good question. The bias toward comparing strings as numbers if either operand is a number or a boolean stems from the HTTP header and numeric-string HTML form field use-cases. Not good reasons, again, but that's how JS "works" :-|.
You can see this in the ECMA-262 Edition 5.1 spec, 11.9.3 The Abstract Equality Comparison Algorithm, steps 6 & 7 (read in light of steps 4 & 5):
4. If Type(x) is Number and Type(y) is String, return the result of the comparison x == ToNumber(y).
5. If Type(x) is String and Type(y) is Number, return the result of the comparison ToNumber(x) == y.
6. If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
7. If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
This is all in a big "else clause where Type(x) and Type(y) for x == y are not the same.
Sorry there's no pearl (sic) of wisdom here. In addition to implicit conversions, == and != do not widen operands directly (no intermediate conversions) to the narrowest width that can hold the other operand without data loss. This narrowing string to number is just a botch.
If we fixed this botch, we'd still have:
0 == "0"
1 == "1"
true != "1"
false != "0"
But we would also have what your example wants:
true == "true"
false != ""
Per my calling the preference for number over string conversion a botch, we would not have true == "1" or false == "0", because that narrows from string to number. It's true the narrowing loses no bits, and one can widen 0 back to "0" and 1 back to "1", but I meant to illustrate what removing all number-over-string bias from the implicit conversion spec for == would do.
Would such a change break a lot of code on the web? I'd bet large sums it would.
Some take this botch, on top of any implicit conversion under the hood, as another reason to use === and !== always (because they never convert), and to utterly shun == and !=. Others disagree (especially when testing x == null, a one-operator way to test x === null || x === undefined).
Since the web grows mostly-compatibly until very old forms die off, we're stuck with == and !=, so I say it pays to learn what the sloppy equality operators do. Having done that, it seems to me one may use them where they win: when you know the operands are same-type, e.g.
typeof x == "function", etc.
x == null
And otherwise, use === and !==.
The bias toward comparing strings as numbers if either operand is a number or a boolean stems from the HTTP header and numeric-string HTML form field use-cases. Not good reasons, again, but that's how JS "works" :-|.
One more note: it could be argued that narrowing from string to number would be ok (as in, useful most of the time, and not unsafe) if any non-numeric, non-empty string-to-number implicit conversion attempt threw an exception.
Here's where another path-dependent bias in JS's design bit: no try/catch in JS1 or any ECMA-262 standard till ES3.
The lack of exception handling also meant undefined is imputed for missing obj.foo property get where obj has no such property. That is still biting back, perhaps as much as or more than implicit conversions bite ==. It is also the basis of web JS's winning "object detection" pattern, which fairly beats all other versioning schemes I've seen, especially a-priori ones based on explicit numbering and opt-in.
If only I'd taken the time to add an existential operator for object detection, so one could write
function emulateRequestAnimationFrame(...) {...}
if (!window.requestAnimationFrame?)
window.requestAnimationFrame = emulateRequestAnimationFrame;
IOW, if only I'd made window.noSuchProperty throw but window.noSuchProperty? evaluate to truthy or false (details still TBD, see the "fail-fast object destructuring" thread revival, the Nil idea).
I think some clarification is in order. According to the ECMA specification the entire expression for the if statement (the part within the parentheses) is converted to a boolean.
So imagine it like this:
if (ToBoolean("true" == true)) { //false
vs
if (ToBoolean("true")) { //true
I suppose the rational for why the ToBoolean coercion was added to the if expression was to ensure the expression always evaluates safely and correctly.
ToBoolean coerces a single value to a boolean. A comparison does not coerce each value to a boolean, that wouldn't make sense as you get some pretty strange results. It checks for equality, a different operation. As for why one value isn't converted to boolean when the other is one I am not sure, but try the Mozilla ECMA mailing list: https://mail.mozilla.org/listinfo/es-discuss
See:
http://www.ecma-international.org/ecma-262/5.1/#sec-9.2
http://www.ecma-international.org/ecma-262/5.1/#sec-12.5
http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.1
http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3
http://www.ecma-international.org/ecma-262/5.1/#sec-8.7.1

javascript comparison crises

I came across the following and was unable to grasp the reason, can anyone explain please?
var foo = [0];
console.log(foo == !foo); // true
console.log(foo == foo); // true
The second comparison is simple to explain: foo is equal to itself.
The first one, however, is a bit tricky: foo is an array, which is an object, which evaluates to true when coerced to boolean. So !foo is false. But foo on the left side of the comparison is not being converted to boolean. Both operands are actually converted to numbers during the equality comparison. This is how it evaluates:
[0] == false
[0] == 0
"0" == 0
0 == 0
true
According to MDN, on comparisons with the equality operator ==:
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
I know this explanation sounds superficial. It's actually much more complicated than that, but the basic steps are the ones I listed above. You can see the details on the ECMA-262 specification, particularly on sections 9 and 11.9.
You should use "===" and "!==" instead of "==" and "!="
More explanations there:
Which equals operator (== vs ===) should be used in JavaScript comparisons?
http://net.tutsplus.com/tutorials/javascript-ajax/the-10-javascript-mistakes-youre-making/

Unexpected result from expression using &

Here if
document.write(eval("(2 == 2)"));
it prints true
And
document.write(eval("(2 == 2)&(5<10)"));
it prints 1
Why not it always returns true or false.If conditions increases in string it gives 0 or 1.What is the way to get same type of result.
Because & isn't a logical operator, it's a bitwise (numeric) operator. You probably meant &&, the logical AND operator.
Note that there's no reason for using eval in your code examples. (There's almost never any reason to use eval, there's almost always a better alternative — in this case, you don't even need an alternative.) Removing it would have exactly the same result.
It prints 1 because you used the bitwise and operator (&) instead of the logical and operator (&&).
And because JavaScript is loosely typed it will treat the boolean value true as an int value of 1, which you can check by invoking
document.write(eval("(2 == 2)+(5<10)"));
the result will be
2
& is a bitwise operator and returns a number. Try "(2 == 2)&&(5<10)"

Categories