As per the operator precedence table for JavaScript, I can see that && has higher precedence than ||.
So, for the following code snippet:
let x, y;
let z = 5 || (x = false) && (y = true);
console.log(z); // 5
console.log(x); // undefined
console.log(y); // undefined
I thought that && should be evaluated first and after short-circuiting for the && part, x would be assigned the value false. And then only, || would be tried to be evaluated.
But, from the output of the console.log, I can clearly see that's not the case here.
Can someone please help me what am I missing here?
Thanks in advance.
What the operator precedence of && of || means is that this:
let z = 5 || (x = false) && (y = true);
gets evaluated as:
let z = 5 || ((x = false) && (y = true));
and not as
let z = (5 || (x = false)) && (y = true);
It's not something that indicates when the values on each side of an operator is evaluated. That's done by the mechanics of each individual operator. For example, for ||, it's specified here:
1. Let lref be the result of evaluating LogicalANDExpression.
2. Let lval be ? GetValue(lref).
3. Let lbool be ToBoolean(lval).
4. If lbool is false, return lval.
5. Let rref be the result of evaluating BitwiseORExpression.
6. Return ? GetValue(rref).
It evaluates GetValue(lref) before GetValue(rref) runs.
In other words, operator precedence indicates which tokens are applied to which operator, and in what order, but not how/when a given operator evaluates the values on its left and right side.
Related
According to Mozilla, the === operator has higher precedence than the || operator, which is what I would expect.
However, this statement evaluates to the number 1, rather than false.
let x = 1 || 0 === 0; // x === 1;
You have to wrap in parentheses to get a boolean:
let x = (1 || 0) === 0; // x === false;
What is the explanation?
Note: This is not a duplicate of this question, which does not have anything about equality operators - JavaScript OR (||) variable assignment explanation
Higher operator precedence is like a parenthesis around the operands.
let x = 1 || (0 === 0);
The second part gets never evaluated, because of the truthy value of 1
.
|| is a short circuit operator and conditions are evaluated from left to right.
So in left || right, if the left condition is true, the whole condition is evaluated to true and the right one is never evaluated.
In
let x = 1 || 0 === 0; // x === 1;
x = 1 assigns 1 to x and the second condition after || is never evaluated as if (1) is evaluated to true.
And in
let x = (1 || 0) === 0; // x === false;
(1 || 0) is evaluated to true as if (1) is still evaluated to true.
And then true === 0 is evaluated to false.
So x is valued to false.
From a bug report, I think that the following expression might throw an exception if x is null:
if ( !x || doSomething( x[prop], y[prop] ) === false )
The exception is:
Cannot read property 'prop' of null
... as if the right side of the || is evaluated even if the left side is true.
The javascript reference seems to indicate that that should not happen, but I'm not sure. I've tested that just writing x = null does not (always) crash, but is it guaranteed on every JS engine ?
EDIT:
Same question about
if( x && foo( x[prop] ) === true && bar() === false )
One way to put it is, does :
if( a && b && c )
... evaluates b or c if a === false ? The doc is not clear about that case, only for "a && ( expr1 && expr2 )", not "a && expr1 && expr2"
Full code snippet
var x = null;
var y = {
"p1": "p1",
"p2": "p2"
};
function f() {
return true;
}
for (var propName in y) {
if (x && f(y[propName]) === true && f(y[propName]) === false) {
doSomething(x[propName], y[propName]);
} else if (!x || f(x[propName], y[propName]) === false) {
console.log(y[propName]);
}
}
EDIT2: for completeness, the real (minimized) code that run in the browser
function a(c, b, e, f) {
for (var d in b) {
if (c && _.isObject(b[d]) === true && _.isArray(b[d]) === false) {
a(c[d], b[d], e, d + ".")
} else {
if (!c || _.isEqual(c[d], b[d]) === false) {
e.push({
name: f + d,
value: b[d]
})
}
}
}
return e
}
The Javascript || operator is short-circuiting. The right-hand side will not evaluate if the left-hand side is true. That's a fundamental property of the operator and should be equally implemented across all engines.
Therefore, the right-hand side will only evaluate if x is truthy, and all truthy values in Javascript should be subscriptable without error.
Having said that, y is completely unknown in this example and might throw an error.
"Is it guaranteed on every JS engine?"
We can't actually know that for sure, but the standard defines, how these operators should be implemented.
Logical OR:
Let lref be the result of evaluating LogicalORExpression.
Let lval be GetValue(lref).
If ToBoolean(lval) is true, return lval.
Let rref be the result of evaluating LogicalANDExpression.
Return GetValue(rref).
http://es5.github.io/#x11.11
Item 3 doesn't leave any room to doubts, lval is returned immediately if lref can be evaluated to truthy, and rref will never be evaluated.
if (typeof y != 'undefined' && typeof x != 'undefined' && x !== null && y !== null) {
if (doSomething( x[prop], y[prop] ) === false) {
//do stuff
}
}
do the safety check before. this should be working
but note:
if your prop Attribute does not exist, this will return an error too!
greetings
I know you can do ternary expressions in Javascript for an if - else statement, but how about an else- else if- else statement? I thought that surely this would be supported but I haven't been able to find any info about it and wasn't able to get it to work just hacking around.
In contrast to Robby Cornelissen's answer - there is no problems with readability if you format it properly (and not writing PHP, since it messed up the operator by making it left-associative in contrast to all other languages that have that construct):
var y =
x == 0 ? "zero" :
x == 1 ? "one" :
"other";
EDIT
What I was looking for is a shorter version of "if expression 1 is true, return expression 1. Else if expression 2 is true, return expression 2. Else return expression 3". Is there no clean way to do this?
There is: expression1 || expression2 || expression3. (It would have been nice if you had put this into your question in the first place.) This is commonly used for default values:
var defaults = null;
function hello(name) {
var displayName = name || (defaults && defaults.name) || "Anonymous";
console.log("Hello, " + displayName + ".");
}
hello("George");
// => Hello, George.
hello();
// => Hello, Anonymous.
defaults = {};
hello();
// => Hello, Anonymous.
defaults.name = "You"
hello();
// => Hello, You.
However, it is important to be aware of the conditions for truthiness. For example, if you expect "" or 0 to be a valid value that does not need to be replaced by a default, the code will fail; this trick only works when the set of possible non-default values is exactly the set of truthy values, no more and no less. E.g.
function increment(val, by) {
return val + (by || 1); // BUG
}
increment(10, 4);
// => 14
increment(10, 1);
// => 11
increment(10);
// => 11
increment(10, 0);
// => 11 <-- should be 10
In this case you need to be explicit:
function increment(val, by) {
return val + (typeof(by) === "undefined" ? 1 : by);
}
I wouldn't recommend it because of readability, but you could just nest ternary operators:
var y = (x == 0 ? "zero" : (x == 1 ? "one" : "other"));
This would be the equivalent of:
var y;
if (x == 0) {
y = "zero";
} else if (x == 1) {
y = "one";
} else {
y = "other";
}
You can extend a ternary condition if you're good. It gets to be messy though.
var number = 5;
var power = 2;
var ans = Math.pow(number,power);
var suggest = ( ans == 5 ? 5 : ans == 10 ? 10 : ans == 15 ? 15 : ans == 25 ? "works" : null);
console.log(suggest);
I may have added to many because I'm on my phone haha but try it in your developer panel.
I obviously got something terribly wrong here so I'll appreciate any good 'ol advice.
How come that if I write
var x='';
var y="12345";
(y.substring(0, 3) === "000"||"999") ? x=1: x=0;
console.log (x, y.substring(0, 3));
The answer would be 1 "123"
instead of 0 "123"?
Thanks y'all!
First the ternary operator syntax is not how you use it normally and you'll have to make two comparisons instead of one.
var str = y.substring(0, 3);
x = (str === "000"|| str === "999") ? 1 : 0;
MDN
For condition ? expr1 : expr2
If condition is true, the operator returns the value of expr1;
otherwise, it returns the value of expr2.
The or operater works like this: a || b
Where each statement is isolated from eachother, basically you can make i more visible like this:
var c1 = y.substring(0, 3) === "000";
var c2 = "999";
if ( c1 || c2 ) { x = 1; } else { x = 0; };
See the problem here?
I would rewrite your statement so something like this:
x = ["000", "999"].indexOf(y.slice(0, 3)) > -1 ? 1 : 0;
Note how I'm using Array.prototype.indexOf to test multiply cases:
["000", "999"].indexOf(y.slice(0, 3)) // returns the index of the array or -1 if not in the array.
A book states following rules for OR:
If the first operand is an object, then the first operand is returned.
If the first operand evaluates to false, then the second operand is returned.
If both operands are objects, then the first operand is returned.
If both operands are null, then null is returned.
If both operands are NaN, then NaN is returned.
If both operands are undefined, then undefined is returned.
However I observed following behavior while coding:
var result18 = (NaNVar || undefinedVar); //undefined
var result19 = (NaNVar || nullVar); //null
var result20 = (undefinedVar || NaNVar); //NaN
var result21 = (undefinedVar || nullVar); //null
var result22 = (nullVar || NaNVar); //NaN
var result23 = (nullVar || undefined); //undefined
How can I justify this behavior for those rules?
This rule is the key:
If the first operand evaluates to false, then the second operand is
returned.
All of your left hand side values evaluate to false, so the right hand side is returned.
Here's a good definition from MDN if it helps you:
expr1 || expr2
Returns expr1 if it can be converted to true; otherwise, returns
expr2. Thus, when used with Boolean values, || returns true if either
operand is true; if both are false, returns false.
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Logical_Operators
Sorry for the confusion around the description in the book. I was trying to enumerate edge cases and I see how that could cause some confusion.
You can accurately describe the operation using only two rules: if the first argument is truthy then return the first argument; if the first argument is falsy return the second argument. Your third rule doesn't just apply to this operator, an undeclared variable will always causing her to be thrown when you try to use it. It doesn't matter what you try to use it for (with the exception of typeof and delete, which work fine in undeclared variables).
Your book has chosen a terrible way to describe the logical OR operator.
For example, this rule is far too limiting.
If the first operand is an object, then the first operand is returned.
The operator does not do any sort of type check. It doesn't care if the first or second operand is an "object". It only cares about how they coerce to a boolean.
Take this example.
"foobar" || false
The first operand is a string, not an object, but it will coerce to the boolean true, so the first operand is returned.
Boolean("foobar"); // true
Your book is walking through bullet points as though it was following some sort of specified algorithm. There is no such algorithm. The comparison is strictly based on Boolean coercion.
To put it simply,
it evaluates operands from left to right until one is found that will coerce to true or until it runs out of operands.
the last operand evaluated is returned (uncoerced)
11.11 Binary Logical Operators
Let lref be the result of evaluating LogicalORExpression.
Let lval be GetValue(lref).
If ToBoolean(lval) is true, return lval.
Let rref be the result of evaluating LogicalANDExpression.
Return GetValue(rref).
Yes after observing the results, I concluded two simple rules:
//1: if the first operand evaluates to true then it is returned (here it means actual //value of operand is returned but not the evaluated value that is true)
//following values evaluates to ture: non-empty string, non-zero number and //none of these values- NaN, null, undefined
var result = ("Mahesh" || false) //"Mahesh"
var result = ("Mahesh" || true) //"Mahesh"
var result = ("Mahesh" || undefined) //"Mahesh"
var result = ("Mahesh" || null) //"Mahesh"
var result = ("Mahesh" || NaN) //"Mahesh"
var result = (5 || false) //5
var result = (5 || true) //5
var result = (5 || null) //5
var result = (5 || NaN) //5
var result = (5 || undefined) //5
//2: if first operand evaluates to false then the value of second operand is //returned, again without evaluating it
//following values evaluate to false: empty string (""), number zero (0), null, //NaN, undefined or false)
var result = (false || NaN); //NaN
var result = (false || null); //null
var result = (false || undefined); //undefined
var result = (false || "Mahesh"); //Mahesh
var result = (false || 5); //5
var result = (NaN || false); //false
var result = (NaN || true); //true
var result = (NaN || NaN); //NaN
var result = (NaN || null); //null
var result = (NaN || undefined); //undefined
var result = (NaN || "Mahesh"); //Mahesh
var result = (NaN || 5); //5
var result = (null || false); //false
var result = (null || true); //true
var result = (null || NaN); //NaN
var result = (null || null); //null
var result = (null || undefined); //undefined
var result = (null || "Mahesh"); //Mahesh
var result = (null || 5); //5
var result = (undefined || false); //false
var result = (undefined || true); //true
var result = (undefined || NaN); //NaN
var result = (undefined || null); //null
var result = (undefined || undefined); //undefined
var result = (undefined || "Mahesh"); //Mahesh
var result = (undefined || 5); //5
var result = (0 || false); //false
var result = (0 || true); //true
var result = (0 || NaN); //NaN
var result = (0 || null); //null
var result = (0 || undefined); //undefined
var result = (0 || "Mahesh"); //Mahesh
var result = (0 || 5); //5
var result = ("" || false); //false
var result = ("" || true); //true
var result = ("" || NaN); //NaN
var result = (""|| null); //null
var result = (""|| undefined); //undefined
var result = ("" || "Mahesh"); //Mahesh
var result = ("" || 5); //5
//Note: if the first operand evaluates to false and if the second operand is undeclared
//variable then it will cause an error
var result = (false || undeclaredVar); //error
I think that's all in it in simpler words. Can anyone here confirm if I have got right understanding?
I tried this in IE10, hope things will be consistent across other browsers.