I was reviewing the code for an angularjs factory to better understand how it works. The code contains an if
statement that I don\'t fully understand.
I
Meanwhile, ^
is the bitwise XOR operator.
When dealing with numbers smaller than 2, ^
will work like a boolean OR (||
) if you consider 0
= false
and 1
= true
.
This is a horribly unreadable way to write out the boolean value of a variable, and then convert it using unary conversion to give a 0/1 number result.
Consider:
+!!true; //this converts true to false, then to true again, and true is 1 when converted
+!!0; //converts 0 (falsy) to true, then false, and then the numeric 0
Technically speaking !!
is not its own operator, it's just the NOT (!
) operator twice.
Unary conversion: ECMA spec doc a unary plus attempts to convert to an integer. Number()
would also be a valid conversion.
!! converts a value to a boolean (true or false). +
then converts that boolean to a number, either 1
for true
or 0
for false.
> +true
1
> +false
0
Personally I find it clearer to write something like this, when dealing with two booleans:
if (!config.template == !config.templateUrl) {
throw ...
}
Code clarity and readability be damned, apparently.
! is the logical not operator. It is a unary operator that converts its operand to a boolean and then negates it. !!
is just that operator twice, the second !
undoes the negation, so the end result is just conversion to a boolean.
+ is the unary plus operator, which converts it's operand to a number. In the case of a boolean, false becomes 0
, and true becomes 1
.
So, +!!(expression)
evaluates to 1
if the expression is truthy and 0
if the expression is falsy.
if ((+!!config.template) + (+!!config.templateUrl) !== 1) {
0 + 0 !== 1 true
0 + 1 !== 1 false
1 + 0 !== 1 false
1 + 1 !== 1 true
is equal to
if (!config.template === !config.templateUrl) {
despite the content of the two properties.
+!! uses implicit conversion to cast a value as a 0 or 1 depending on its boolean value
For the most part, this is to check for existence. For example, an empty string is false (!!"" === false
), and so is undefined, and a number of others. Those are the main two though
"Falsey" conversions
+!!"" === 0
+!!false === 0
+!!0 === 0
+!!undefined === 0
+!!null === 0
+!!NaN === 0
"Truthy" conversions
+!!1 === 1
+!!true === 1
+!!"Foo" === 1
+!!3.14 === 1
+!![] === 1
+!!{} === 1
if ((+!!config.template) + (+!!config.templateUrl) !== 1)
Hopefully this is making more sense at this point. The object config
has two properties we are examining. .template
and .templateUrl
. The implicit cast to a 0 or 1 using +!!
is going to be added and then compared to ensure that it is not 1 (which means it is either 0 or 2) - the properties can either both be on or off but not different.
The truth table here is as follows:
template templateUrl (+!!) + (+!!) !==1
"foo" "foo" 1 + 1 true
undefined undefined 0 + 0 true
undefined "" 0 + 0 true
"" undefined 0 + 0 true
12 "" 1 + 0 false
"" 12 0 + 1 false
undefined "foo" 0 + 1 false
"" "foo" 0 + 1 false
"foo" "" 1 + 0 false
"foo" undefined 1 + 0 false
A much simpler method to all of this would have been to just use the implicit boolean conversion
if (!config.template === !config.templateUrl)