Why is there no logical xor in JavaScript?
Why is there no logical xor in JavaScript?
JavaScript traces its ancestry back to C, and C does not have a logical XOR operator. Mainly because it's not useful. Bitwise XOR is extremely useful, but in all my years of programming I have never needed a logical XOR.
If you have two boolean variables you can mimic XOR with:
if (a != b)
With two arbitrary variables you could use !
to coerce them to boolean values and then use the same trick:
if (!a != !b)
That's pretty obscure though and would certainly deserve a comment. Indeed, you could even use the bitwise XOR operator at this point, though this would be far too clever for my taste:
if (!a ^ !b)
Javascript has a bitwise XOR operator : ^
var nb = 5^9 // = 12
You can use it with booleans and it will give the result as a 0 or 1 (which you can convert back to boolean, e.g. result = !!(op1 ^ op2)
). But as John said, it's equivalent to result = (op1 != op2)
, which is clearer.
There are no real logical boolean operators in Javascript (although !
comes quite close). A logical operator would only take true
or false
as operands and would only return true
or false
.
In Javascript &&
and ||
take all kinds of operands and return all kinds of funny results (whatever you feed into them).
Also a logical operator should always take the values of both operands into account.
In Javascript &&
and ||
take a lazy shortcut and do not evaluate the second operand in certain cases and thereby neglect its side effects. This behavior is impossible to recreate with a logical xor.
a() && b()
evaluates a()
and returns the result if it's falsy.
Otherwise it evaluates b()
and returns the result. Therefore the returned result is truthy if both results are truthy, and falsy otherwise.
a() || b()
evaluates a()
and returns the result if it's truthy.
Otherwise it evaluates b()
and returns the result. Therefore the returned result is falsy if both results are falsy, and truthy otherwise.
So the general idea is to evaluate the left operand first. The right operand only gets evaluated if necessary. And the last value is the result. This result can be anything. Objects, numbers, strings .. whatever!
This makes it possible to write things like
image = image || new Image(); // default to a new Image
or
src = image && image.src; // only read out src if we have an image
But the truth value of this result can also be used to decide if a "real" logical operator would have returned true or false.
This makes it possible to write things like
if (typeof image.hasAttribute === 'function' && image.hasAttribute('src')) {
or
if (image.hasAttribute('alt') || image.hasAttribute('title')) {
But a "logical" xor operator (^^
) would always have to evaluate both operands. This makes it different to the other "logical" operators which evaluate the second operand only if necessary. I think this is why there is no "logical" xor in Javascript, to avoid confusion.
So what should happen if both operands are falsy? Both could be returned. But only one can be returned. Which one? The first one? Or the second one? My intuition tells me to return the first but usually "logical" operators evaluate from left to right and return the last evaluated value. Or maybe an array containing both values?
And if one operand is truthy and the other operand is falsy, an xor should return the truthy one. Or maybe an array containing the truthy one, to make it compatible with the previous case?
And finally, what should happen if both operands are truthy? You would expect something falsy. But there are no falsy results. So the operation shouldn't return anything. So maybe undefined
or .. an empty array? But an empty array is still truthy.
Taking the array approach you would end up with conditions like if ((a ^^ b).length !== 1) {
. Very confusing.
The XOR of two booleans is simply whether they are different, therefore:
Boolean(a) !== Boolean(b)
Convert values into Boolean form and then take bitwise XOR:
Boolean(a) ^ Boolean(b) // === 0 | 1
Note that the result of this expression is a number and not a Boolean.
Bitwise XOR also works with non-Boolean values, but remember that this is a bitwise operator, and not a logical one. Using non-bools may not go as you first expect:
(5 ^ 3) === 6 // true