Javascript true/false function - javascript

I'm new in JavaScript, and I just discovered strange behavior that I can't understand:
var magicVar = Math.sin;
magicVar == true; // it returns false
magicVar === true; // it returns false
if (magicVar) console.log("???"); // it prints "???"
What is happening?
Thank you.

var magicVar = Math.sin;
From here on magicVar is a reference to the Math.sin function, which is actually an Object (see Function on MDN)
The Function constructor creates a new Function object. In JavaScript
every function is actually a Function object.
magicVar == true; // it returns false
This is false: from the Equality comparison and sameness on MDN if you compare an operand of type Object with an operand of type Boolean using the == operator you always get false (look at the Loose equality using == table).
[EDIT] As Bergi pointed out in the comments you could actually in some cases have objects that loosely compared to a boolean value return true.
What is actually happening behind the scenes is that the comparison algorithm described in ES6 §7.2.12 is applied.
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.
In your case what happens is:
magicVar == true
magicVar == Number(true) // Step 9. x == ToNumber(y).
magicVar == 1
toPrimitive(magicVar) == 1 // Step 11. ToPrimitive(x) == y.
magicVar.toString() == 1
"function sin() { [native code] }" == 1
Number("function sin() { [native code] }") == 1 // Step 7. ToNumber(x) == y.
NaN === 1 // Step 3. x === y.
false
But comparing for example to an object like:
{
valueOf: function(){
return 1;
}
}
you would get:
true == {valueOf(){return 1;}} // it returns true
magicVar === true; // it returns false
This is trivially false, the types of the operands are different and the === operator checks for the operands to have same types and values.
if (magicVar) console.log("???"); // it prints "???"
As all the others answers have said magicVar here is in a Boolean context and gets coerced to true

magicVar is true as a value. It is true in the sense that it's not empty, it's not null or undefined. So, any non-null value would be a true value. But is this value equal to true ? No. It is true as a boolean not true as a value.
To summarize:
magicVar == true || magicVar === true; // returns false
Boolean(magicVar); // returns true

The concept to grasp here is the difference between truthy and falsy values compared to true and false booleans. See here for truthy and here for falsy
Long story short, an if condition passes if the expression is truthy, and magicVar is a function (truthy).

magicVar is not equal to true.
It is equal to the Math.sin function.
So, in the if statement, it is evaluated to a truthy value (it is defined and does not evaluate to a falsy value).
http://www.sitepoint.com/javascript-truthy-falsy/

Comparison function Math.sin with boolean constant return false, because this variables have different types.
But when you put not-bool variable to if condition: for example if (Math.sin)... or if (window)... this return true if variable not equal null.

Related

Why ![] and [] == false yelds different results? [duplicate]

Empty arrays are true but they're also equal to false.
var arr = [];
console.log('Array:', arr);
if (arr) console.log("It's true!");
if (arr == false) console.log("It's false!");
if (arr && arr == false) console.log("...what??");
I guess this is due to the implicit conversion operated by the equality operator.
Can anyone explain what's going on behind the scenes?
You're testing different things here.
if (arr) called on object (Array is instance of Object in JS) will check if the object is present, and returns true/false.
When you call if (arr == false) you compare values of this object and the primitive false value. Internally, arr.toString() is called, which returns an empty string "".
This is because toString called on Array returns Array.join(), and empty string is one of falsy values in JavaScript.
Regarding the line:
if (arr == false) console.log("It's false!");
Maybe these will help:
console.log(0 == false) // true
console.log([] == 0) // true
console.log([] == "") // true
What I believe is happening is that the boolean false is coerced to 0 for comparison with an object (the left-hand side). The object is coerced to a string (the empty string). Then, the empty string is coerced into a number, as well, namely zero. And so the final comparison is 0 == 0, which is true.
Edit: See this section of the spec for details on exactly how this works.
Here's what's happening, starting at rule #1:
1. If Type(x) is different from Type(y), go to step 14.
The next rule that applies is #19:
19. If Type(y) is Boolean, return the result of the comparison x ==
ToNumber(y).
The result of ToNumber(false) is 0, so we now have:
[] == 0
Again, rule #1 tells us to jump to step #14, but the next step that actually applies is #21:
21. If Type(x) is Object and Type(y) is either String or Number, return the
result of the comparison
ToPrimitive(x)== y.
The result of ToPrimitive([]) is the empty string, so we now have:
"" == 0
Again, rule #1 tells us to jump to step #14, but the next step that actually applies is #17:
17. If Type(x) is String and Type(y) is Number, return the result of the
comparison ToNumber(x)== y.
The result of ToNumber("") is 0, which leaves us with:
0 == 0
Now, both values have the same type, so the steps continue from #1 until #7, which says:
7. If x is the same number value as y, return true.
So, we return true.
In brief:
ToNumber(ToPrimitive([])) == ToNumber(false)
To supplement Wayne's answer and to try to explain why ToPrimitive([]) returns "", it's worth considering two possible types of answers to the 'why' question. The first type of answer is: "because the specification says this is how JavaScript will behave." In the ES5 spec, section 9.1, which describes the result of ToPrimitive as a default value for an Object:
The default value of an object is retrieved by calling the [[DefaultValue]] internal method of the object, passing the optional hint PreferredType.
Section 8.12.8 describes the [[DefaultValue]] method. This method takes a "hint" as an argument, and the hint can be either String or Number. To simplify the matter by dispensing with some details, if the hint is String, then [[DefaultValue]] returns the value of toString() if it exists and returns a primitive value and otherwise returns the value of valueOf(). If the hint is Number, the priorities of toString() and valueOf() are reversed so that valueOf() is called first and its value returned if it's a primitive. Thus, whether [[DefaultValue]] returns the result of toString() or valueOf() depends on the specified PreferredType for the object and whether or not these functions return primitive values.
The default valueOf() Object method just returns the object itself, which means that unless a class overrides the default method, valueOf() just returns the Object itself. This is the case for Array. [].valueOf() returns the object [] itself. Since an Array object is not a primitive, the [[DefaultValue]] hint is irrelevant: the return value for an array will be the value of toString().
To quote David Flanagan's JavaScript: The Definitive Guide, which, by the way, is a superb book that should be everyone's first place to get answers to these types of questions:
The details of this object-to-number conversion explain why an empty array converts to the number 0 and why an array with a single element may also convert to a number. Arrays inherit the default valueOf() method that returns an object rather than a primitive value, so array-to-number conversion relies on the toString() method. Empty arrays convert to the empty string. And the empty string converts to the number 0. An array with a single element converts to the same string that that one element does. If an array contains a single number, that number is converted to a string, and then back to a number.
The second type of answer to the "why" question, other than "because the spec says", gives some explanation for why the behavior makes sense from the design perspective. On this issue I can only speculate. First, how would one convert an array to a number? The only sensible possibility I can think of would be to convert an empty array to 0 and any non-empty array to 1. But as Wayne's answer revealed, an empty array will get converted to 0 for many types of comparisons anyway. Beyond this, it's hard to think of a sensible primitive return value for Array.valueOf(). So one could argue that it just makes more sense to have Array.valueOf() be the default and return the Array itself, leading toString() to be the result used by ToPrimitive. It just makes more sense to convert an Array to a string, rather than a number.
Moreover, as hinted by the Flanagan quote, this design decision does enable certain types of beneficial behaviors. For instance:
var a = [17], b = 17, c=1;
console.log(a==b); // <= true
console.log(a==c); // <= false
This behavior allows you to compare a single-element array to numbers and get the expected result.
console.log('-- types: undefined, boolean, number, string, object --');
console.log(typeof undefined); // undefined
console.log(typeof null); // object
console.log(typeof NaN); // number
console.log(typeof false); // boolean
console.log(typeof 0); // number
console.log(typeof ""); // string
console.log(typeof []); // object
console.log(typeof {}); // object
console.log('-- Different values: NotExist, Falsy, NaN, [], {} --');
console.log('-- 1. NotExist values: undefined, null have same value --');
console.log(undefined == null); // true
console.log('-- 2. Falsy values: false, 0, "" have same value --');
console.log(false == 0); // true
console.log(false == ""); // true
console.log(0 == ""); // true
console.log('-- 3. !NotExist, !Falsy, and !NaN return true --');
console.log(!undefined); // true
console.log(!null); // true
console.log(!false); // true
console.log(!""); // true
console.log(!0); // true
console.log(!NaN); // true
console.log('-- 4. [] is not falsy, but [] == false because [].toString() returns "" --');
console.log(false == []); // true
console.log([].toString()); // ""
console.log(![]); // false
console.log('-- 5. {} is not falsy, and {} != false, because {}.toString() returns "[object Object]" --');
console.log(false == {}); // false
console.log({}.toString()); // [object Object]
console.log(!{}); // false
console.log('-- Comparing --');
console.log('-- 1. string will be converted to number or NaN when comparing with a number, and "" will be converted to 0 --');
console.log(12 < "2"); // false
console.log("12" < "2"); // true
console.log("" < 2); // true
console.log('-- 2. NaN can not be compared with any value, even if NaN itself, always return false --');
console.log(NaN == NaN); // false
console.log(NaN == null); // false
console.log(NaN == undefined); // false
console.log(0 <= NaN); // false
console.log(0 >= NaN); // false
console.log(undefined <= NaN); // false
console.log(undefined >= NaN); // false
console.log(null <= NaN); // false
console.log(null >= NaN); // false
console.log(2 <= "2a"); // false, since "2a" is converted to NaN
console.log(2 >= "2a"); // false, since "2a" is converted to NaN
console.log('-- 3. undefined can only == null and == undefined, and can not do any other comparing even if <= undefined --');
console.log(undefined == null); // true
console.log(undefined == undefined); // true
console.log(undefined == ""); // false
console.log(undefined == false); // false
console.log(undefined <= undefined); // false
console.log(undefined <= null); // false
console.log(undefined >= null); // false
console.log(0 <= undefined); // false
console.log(0 >= undefined); // false
console.log('-- 4. null will be converted to "" when <, >, <=, >= comparing --');
console.log(12 <= null); // false
console.log(12 >= null); // true
console.log("12" <= null); // false
console.log("12" >= null); // true
console.log(0 == null); // false
console.log("" == null); // false
console.log('-- 5. object, including {}, [], will be call toString() when comparing --');
console.log(12 < {}); // false, since {}.toString() is "[object Object]", and then converted to NaN
console.log(12 > {}); // false, since {}.toString() is "[object Object]", and then converted to NaN
console.log("[a" < {}); // true, since {}.toString() is "[object Object]"
console.log("[a" > {}); // false, since {}.toString() is "[object Object]"
console.log(12 < []); // false, since {}.toString() is "", and then converted to 0
console.log(12 > []); // true, since {}.toString() is "", and then converted to 0
console.log("[a" < []); // false, since {}.toString() is ""
console.log("[a" > []); // true, since {}.toString() is ""
console.log('-- 6. According to 4 and 5, we can get below weird result: --');
console.log(null < []); // false
console.log(null > []); // false
console.log(null == []); // false
console.log(null <= []); // true
console.log(null >= []); // true
In if (arr), it is always evaluated (ToBoolean) to true if arr is an object because all objects in JavaScript are truthy. (null is not an object!)
[] == false is evaluated in iterative approach. At first, if one side of == is primitive and the other is object, it converts object to primitive at first, then converts both sides to Number if both sides are not string (string comparison is used if both sides are strings). So the comparison is iterated like, [] == false -> '' == false -> 0 == 0 -> true.
Example:
const array = []
const boolValueOfArray = !!array // true
It happens because
ToNumber(ToPrimitive([])) == ToNumber(false)
[] is empty Array object → ToPrimitive([]) → "" → ToNumber("") → 0
ToNumber(false) → 0
0 == 0 → true
An array with elements (regardless if 0, false or another empty array), always resolves to true using Abstract Equality Comparison ==.
1. [] == false; // true, because an empty array has nothing to be truthy about
2. [2] == false; // false because it has at least 1 item
3. [false] == false; // also false because false is still an item
4. [[]] == false; // false, empty array is still an item
But using a Strict Equality Comparison ===, you are attempting to evaluate the variable's content as well as its data type that is why:
1. [] === false; // false, because an array (regardless of empty or not) is not strictly comparable to boolean `false`
2. [] === true; // false, same as above, cannot strictly compare [] to boolean `true`
3. [[]] === false; // true, because see #1
It seems no one is talking about why
(arr && arr == false) is true. Just for people who are not sure about the operator precedence, accourding to MDN, == has higher precedence than &&, so it is actually (arr && (arr == false)). After wildcard's answer , it's true && true, so it's true.
var arr = [];
if (arr && arr == false) console.log("...what??");
You can empty a JavaScript Array by referencing it to a new array, using list = [] or deleting the elements of the currently referenced array list.length = 0.
Source: JavaScript Empty Array
None of the above helped me, when trying to use the knockout.js mapping plugin, perhaps since an "empty array" isn't really empty.
I ended up using: data-bind="if: arr().length" which did the trick.
This is specific to knockout, not the OP's question, but maybe it will help someone else browsing here in a similar situation.

Javascript array equal to zero but not itself

I've been playing around with arrays in JavaScript and cannot figure out why this happens:
console.log(0 == 0)
//true
console.log([] == 0)
//true
console.log(0 == [])
//true
console.log([] == [])
//false
console.log([] == ![])
// true
The empty array is equal enough to zero both left and right, but why isn't it equal to itself?
I realise that comparing two objects would not result true, but why are they coerced to 0 (or falsy, which shouldn't be the case) if you compare them to 0, while threated as an object if you compare them to the other array?
console.log(0 == [])
//true
You are trying to compare object with an integer, so your object is implicitly typecasted to equivalent integer value that is 0
console.log([] == [])
//false
as two objects are never equal
console.log([] == [])
That will compare whether array1 and array2 are the same array object in memory, which is not what you want.
In order to do what you want, you'll need to check whether the two arrays have the same length, and that each member in each index is identical.
console.log([].length == [].length)
// true
Since the complete answer is never given and I actually understand it now, I'll provide the answer myself.
I found this in the Ecma-262 pdf:
It basically reads that [] == 0 is the same as Number([]) == 0 which is the same as 0 == 0 which is true. This does not apply to strict ===.
There is no rule to compare objects other then rule number one, which is x is the same as y. This means the same in everything, also memory address. Since they are not sharing the same memory address, rule 10 applies (return false).
The comparison x == y, where x and y are values, produces true or
false. Such a comparison is performed as follows:
If Type(x) is the same as Type(y), then
a. 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, return the result of the comparison x == ToPrimitive(y).
If Type(x) is Object and Type(y) is either String, Number, or Symbol, return the result of the comparison ToPrimitive(x) == y.
Return false
This question is handled with the knowledge of object reference and type conversion more properly.First,in javascript, object value is stored by reference.So we can tell it is different from [] and [],because the two array correspond with two different addr in memory.Second, '==' is a not rigorous operation for both left and right, [] and 0 are both transformed to false.

'&&' operator in Javascript vs in Java

I am currently reading JavaScript from JavaScript: The Definitive Guide. On page 76, there is this statement written,
o && o.x // => 1: o is truthy, so return value of o.x
Being a Java programmer I want to ask, why is it returning 1 instead of 'true' In Java this was not the case, but anyway I know JavaScript is different but the Logical AND mechanism is the same everywhere.(In C it returns 1 as true.)
I am asking is, why does it makes sense for this behavior at all?
Is there any way, I can ensure to return only the true or false values?
As per the Spec for Binary Logical Operators
The value produced by a && or || operator is not necessarily of type
Boolean. The value produced will always be the value of one of the two
operand expressions.
This is a feature used in javascript a lot, one common use case is to assign a default value to a variable if it is not defined. Like assume you are expecting a options object as a param but is not mandatory so the user may not pass it
function x(options){
options = options || {};
//now your can access optionx.a without fearing whether options is undefined
}
You can do something like !!(o && o.x) to always get a true/false
if we have:
expr1 && expr2
&& Returns 'expr1' if it can be converted to false; otherwise, returns 'expr2'. Thus, when used with Boolean values, && returns true if both operands are true; otherwise, returns false.
So if we have:
Var xyz = a && b; //where a is undefined(false) and b is 123, then it will allocate undefined.
In case of if statements, these values are specifically converted to boolean.
Refer the following link: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators"
You can use Boolean(o && o.x); to get true or false
It's returning the value of the right operand. If the value of o.x was true, it would return true. If the value of o.x was 'banana', it would return 'banana'.
var o = {x:true};
console.log(o && o.x);
Technically && operator implemented to return the first operand if it's value is not zero, and returns the second operand otherwise in a lot of languages including Java, C/C++, Javascript, Perl...
While most of the languages accepts any type of operands, Java forces you to use boolean operands so it always returns true or false.
In order to force a boolean result in Javascript use:
Boolean(a && b)
This behaviour is called coercion. Coercion is the action of forcing an object to behave like other type, and the logical operators can produce coercion when trying to access the value of the object to be evaluated.
It is important to remember the table for truthy and falsy values, because due to coercion, different results can be obtained.
false produces false
0 produces false
“” produces false
NaN produces false
null produces false
undefined produces false
Everything else produces true, including the text "0" and "false", functions, arrays and empty objects.
Given the rules for logic operators, exists short-circuit evaluation in JavaScript, e.g:
0 && 1; //-> 0, falsy value
!(0 && 1); //-> true, falsy value negated
!!(0 && 1); //-> false, boolean falsy value
void 0 && alert("1") //-> undefined (void produces undefined)
[] && Math.ceil(9.1) //-> 10, truthy value
{} && someFunc() //-> the result of someFunc()
0 || null; //-> null, falsy value
null || "0"; //-> "0", truthy value
"" || void 1; //-> undefined, falsy value
!!(+"5px" || {}); //-> true, boolean truthy value
Coercion is useful when you have to validate default values, in order to prevent errors, e.g.
function divide (a, b) {
a = +a; //-> coerced to number
b = +b; //-> coerced to number
if (!a) return 0; //-> NaN or zero
return b && a / b; //-> b must be a number different from zero
}
divide(); //-> 0
divide(5); //-> NaN, no error thrown!
divide(5, "a"); //-> NaN, no error thrown!
divide(5, 0); //-> 0, division by zero prevented!
divide(49, 6); //-> 8.1666
If you want to prevent returning NaN, just add another coercion in the return statement:
return (b && a / b) || 0;
You can check other coercion cases: JavaScript Coercion
Happy coding!

Undefined or null variable in function

In these example I am ruining a function with conditions;
function validPrice(price)
{
if(price=="" || price==0 || price==null || price==undefined )
{
//do something
}
else
{
// do something different
}
}
var priceNew = $("li#listedProd").attr("price");
validPrice(priceNew);
My question is what are the different in these condition price=="" || price==0 || price==null || price==undefined
Whoever first wrote that code was either
a) Being very defensive against future use.
b) Didn't understand how attr works.
The method attr (or the underlying call of getAttribute will return either
A string value, of the attribute is found
null, if it is not.
Importantly, should there have been a 0 value, it would be a string 0, and thus not caught in the test below -- caught against the price == 0 test because the system would have automatically converted it to a number as part of the == compare.
if(price=="" || price==0 || price==null || price==undefined )
And, due to the way that conversions work internally, those tests don't work as intended. == and === are different. It should be:
if(price === "" || price === 0 || price === null || price === undefined )
All of which can easily be reduced to simply "price" due how how coercion to boolean work:
if (!price)
Or, if you want to catch "0" values
if (!price || +price === 0)
(the +price forces price to be a number, so we catch 0 0.00 and other variations.)
Let's look at your conditional statement term-by-term:
price == ""
This is true if price is the empty string, and false otherwise.
price == 0
This is true if price is the integer 0, the string "0", or the empty string, and false otherwise. You should change this comparison to price === 0 if you want to catch when price is the integer 0.
price == null
This is true if price is passed to your function and is of the type null, and false otherwise.
price == undefined
Note: You should probably make this comparison via price === undefined to see that price is both undefined and has the type undefined.
This is true if price is not passed to your function, or if price is otherwise undefined, and false otherwise.
I would recommend just making the entire conditional statement !price:
function validPrice(price) {
if (!price) {
//do something
}
else {
// do something different
}
};
var priceNew = $("li#listedProd").attr("price");
validPrice(priceNew);
EDIT added definitions
JavaScript has 2 operators that test for equality (and conversely 2 operators that test for inequality.)
A strict equality comparison (e.g., ===) is only true if the operands are of the same type AND if the object is actually the same object, or the primitives have the same value (e.g., 0 === 0, and 'a' === 'a' are both true). *Note there is a slight exception to this rule for NaN (see below)
An abstract equality comparison (e.g. ==) converts the operands to the same Type (if they aren't already) before making a strict equality comparison. If however one of the operands is null or undefined, the comparison is true if and only if the other operand is either null or undefined.
So in short the == equality operator is considered by many to be unsafe. To see the difference checkout http://dorey.github.io/JavaScript-Equality-Table/
For instance '' ==: 0, '', [], false, [[]]
And 0 ==: false, 0, '0', '', [], [[]], [0]
While null ==: null, undefined
And finally undefined ==: null, undefined
It is better to use strict equal === or just test for a falsey value ('', 0, undefined, null, false)
if (!price) { //do something }
Below I have provided more details about strict vs abstract equality comparison
details came from ECMA-262, Edition 5 11.9.1
Summary of strict equality algorithm
1. If typeof(x) is different from typeof(y), return false.
2. If typeof(x) is undefined, return true.
3. If x is null, return true.
4. If typeof(x) is number, then
a. If x is NaN, return false.
b. If y is NaN, return false.
c. If x is the same Number value as y, return true.
d. If x is +0 and y is -0, return true.
e. If x is -0 and y is +0, return true.
f. Return false.
5. If typeof(x) is string, then
a. If x and y are exactly the same sequence of characters (same length and same characters in
corresponding positions), return true.
b. Else, return false.
6. If typeof(x) is boolean, then
a. If x and y are both true or both false, return true.
b. Else, return false.
7. If x and y are the same Object value, return true.
8. Return false.
And ... details came from ECMA-262, Edition 5 11.9.1
Summary of Abstract Equality Algorithm
1. If typeof(x) is the same as typeof(y), then
return the result of performing strict equality comparison algorithm x === y.
2. If x is null and y is undefined, return true.
3. If x is undefined and y is null, return true.
4. If typeof(x) is number and typeof(y) is string,
return the result of the comparison x == Number(y).
5. If typeof(x) is string and typeof(y) is number,
return the result of the comparison Number(x) == y.
6. If typeof(x) is boolean, return the result of the comparison Number(x) == y.
7. If typeof(y) is boolean, return the result of the comparison x == Number(y).
8. If typeof(x) is either string or number and typeof(y) is object,
return the result of the comparison x == [[ToPrimitive]](y).
9. If typeof(x) is object and typeof(y) is either string or number,
return the result of the comparison [[ToPrimitive]](x) == y.
10. Return false.
[[ToPrimitive]] is an internal function call
Bullets 8 and 9 basically mean that objects are converted like object.valueOf().toString() so:
{} == '[Object object]'
{ hi: 'hi'} == '[Object object]'
{ valueOf: function(){ return 0; }} == 0
{ toString: function(){ return 'hi'; }} == 'hi'
{ valueOf: function(){ return 0; }, toString: function(){ return 'hi'; }} == 0
Full ecma-262/5.1/#sec-11.9 spec

Short-circuiting an empty array in JS has an unexpected outcome: `[] || true == []`

In my code I assumed the following || short-circuiting was safe:
var $holidayExpandBarOrOpeningHours =
$(".expandBar + .holidayHours_c").prev() || $(".openingHours");
But to my surprise if we short-circuit an empty array with a true statement an empty array is still returned. I will demonstrate with some console code below and my question is why [] || true evaluates to [].
false || "expected"
"expected"
false == []
true
[] || "expected"
[]
typeof([])
"object"
({}) || "expected"
Object {}
({}) == false
false
{} == false
SyntaxError: Unexpected token ==
Part of me thinks that it is because an array is an object which evaluates to true, however if that was the case than based on ({}) == true one would expect [] == true.
Last thing I would like to note is the outcome is the same when using use 'strict' mode.
When converted to a boolean value, [] is true.
> !![]
true
> ![]
false
When converted to a number, [] is 0. That's why comparing it with false returns true: when comparing two values of different types, JavaScript first converts both to numbers and then compares the numbers.
> +[]
0
> +false
0
> +[] == +false
true
This is because || and use == different rules for the conversion.
The logical-or uses ToBoolean while the equality equals uses ToNumber/ToPrimitive.
From 11.11 Binary Logical Operators:
3) If ToBoolean(lval) is true, return lval.
Since ToBoolean([]) is true, [] || x results in []. This is also why if([]) { /* this runs */ }: arrays in JavaScript are "truthy" values.
From 11.9.3 The Abstract Equality Comparison Algorithm:
7) If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
9) [..then] If Type(x) is Object and Type(y) is either String or Number,
return the result of the comparison ToPrimitive(x) == y.
5) [..then] If Type(x) is String and Type(y) is Number,
return the result of the comparison ToNumber(x) == y.
The logic applied to [] == true is ToNumber(ToPrimitive([])) == ToNumber(true).
And the conversion values:
ToBoolean([]) is true
ToNumber(true) is 1
ToPrimitive([]) is an empty string (from DefaultValue/toString)
So:
ToNumber(ToPrimitive([])) == ToNumber(true)
ToNumber("") == 1
0 == 1
false
(Which is a long way to say what John Kugelman said.)
Also, ({}) == true is normally false and follows the same conversions as above.
Under a default environment, ToPrimtive({}) returns a non-empty string (i.e. "[object Object]" as per Object.prototype.toString). This string will evaluate to NaN after ToNumber such that NaN == 1; or false.
See Why an empty Array type-converts to zero? +[] for more details on the ToPrimitive conversion.
An empty array is an object; objects coerced to booleans are true. So
({}) || true; // -> {}
[] || true; // -> []
"" || true; // -> true (empty strings are coerced to false)
Sidenote - the parentheses around {} are required to avoid parsing it as a block.

Categories