Can someone explain to me what is the LeftFirst Boolean flag? when reading the EcmaScript specification about [relational-operators](https://tc39.es/ecma262/#sec-relational-operators" relational-operators definition in ECMAScript") and Abstract Relational Comparison I found something like the LeftFirst Boolean Flag it either becomes true or false but I don't know what is the use of it and for what it's there can someone clearly explain me what is the purpose of the LeftFirst Boolean flag and why it is used in the specification the explanation they have given is not much clear I want to know what is the use of it The leftFirst Boolean Flag and why it's used?
Can someone explain me what is the "LeftFirst" Boolean flag they have defined in the ecmaScript specification
159 Views Asked by Kevin AtThere are 2 best solutions below
On
As the specification says in your link:
The comparison x < y, where x and y are values, produces true, false, or undefined (which indicates that at least one operand is NaN). In addition to x and y the algorithm takes a Boolean flag named LeftFirst as a parameter. The flag is used to control the order in which operations with potentially visible side-effects are performed upon x and y. It is necessary because ECMAScript specifies left to right evaluation of expressions. The default value of LeftFirst is true and indicates that the x parameter corresponds to an expression that occurs to the left of the y parameter's corresponding expression. If LeftFirst is false, the reverse is the case and operations must be performed upon y before x.
For an example of such a thing, consider two objects being compared. For < to be performed on them, both need to be coerced to primitives first, and this primitive coercion is permitted to have side effects which could affect the coercion of the other object. It would be weird, but it's syntactically possible, so the specification needs to specify what must happen in such a situation.
Keep in mind that there's only one version of Abstract Relational Comparison, and that's for expr1 < expr2. There is no separate version for expr1 > expr2. The LeftFirst flag is used when invoking Abstract Relational Comparison to indicate which of the expressions should be evaluated first, so that the left-to-right order of operations is preserved.
Here's an example:
let val = 1;
const obj1 = {
valueOf() {
val++;
return val;
}
};
const obj2 = {
valueOf() {
val++;
return val;
}
};
console.log(obj1 < obj2);
Expressions are evaluated left-to-right. obj1 is evaluated before obj2, so after extracting the primitives, obj1 is less than obj2.
let val = 1;
const obj1 = {
valueOf() {
val++;
return val;
}
};
const obj2 = {
valueOf() {
val++;
return val;
}
};
console.log(obj2 > obj1);
The obj2 > obj1 actually invokes Abstract Relational Comparison obj1 < obj2, with LeftFirst of false. As a result, the right side, obj2 is evaluated first, because it comes first in the source code.
Intuitively, with left-to-right evaluation, we'd expect
obj2 > obj1
to result in
// evaluate obj2, increment val
2 > obj1
// evaluate obj1, increment val
2 > 3
resulting in false.
If it weren't for such a flag, the example above would result in obj1 being evaluated first, and the result would be that obj1 would be less than obj2, and the result of the comparison would be true. But this is not desirable: the expressions should be evaluated left-to-right.
As you noted, it's one of the inputs to the Abstract Relational Comparison algorithm. Its sole purpose is to determine which operand the comparison algorithm is passed to ToPrimitive first, the one on the left (leftFirst = true) or the one on the right (leftFirst = false). The reason is that the Abstract Relational Comparison always does a
<comparison, but it's also used when evaluating>expressions (with the operands reversed). So when handling a>, it needs to be told to use ToPrimitive on the right-hand operand first.You can see it used in the first step of the algorithm:
Also in the description:
For example, if you look at the
<and>operations, the<operation does:That uses the default value of leftFirst, which is
true. Solvalis passed through ToPrimitive beforerval.But the
>operation does:Notice that it does
rval < lval, notlval > rval. But it uses leftFirst =falsebecause it's important that the right operand be passed through ToPrimitive before the left operand, since the real operation islval > rval, solvalshould be passed through ToPrimitive first.In a comment you've said:
It's definitely a bit confusing. The reason that
<and<=don't match (and>and>=don't match) is that the<=/>=operators invert the result of the Abstract Relational Comparison (ARC). So:lval < rvaldoes:lval <= rvaldoeslval > rvaldoes:lval >= rvaldoes:A a final note, let's consider this:
The output you see from that is this:
Using > obj.lval was evaluated obj.rval was evaluated lval was passed through ToPrimitive rval was passed through ToPrimitive true UsingHere's what happens to create that output for
>:obj.lval > obj.rvalexpression is evaluated>operator algorithm is run:lval = obj.lval(Steps 1 & 2), which causes the"obj.lval was evaluated"outputrval = obj.rval(Steps 3 & 4), which causes the"obj.rval was evaluated"outputARC(obj.rval < obj.lval, leftFirst = false)obj.rvalasxandobj.lvalasyfalseand so it does:py = ToPrimitive(y)(Step 2.b), which causes the"lval was passed through ToPrimitive"outputpx = ToPrimitive(x)(Step 2.c), which causes the"rval was passed through ToPrimitive"outputfalse>operator inverts ARC's return value and returnstrueHere's what happens to create the subsequent output for
<:obj.lval < obj.rvalexpression is evaluated<operator algorithm is run:lval = obj.lval(Steps 1 & 2), which causes the"obj.lval was evaluated"outputrval = obj.rval(Steps 3 & 4), which causes the"obj.rval was evaluated"outputARC(obj.lval < obj.rval)(leftFirst defaults to true)obj.lvalasxandobj.rvalasytrueand so it does:px = ToPrimitive(x)(Step 1.a), which causes the"lval was passed through ToPrimitive"outputpy = ToPrimitive(y)(Step 1.b), which causes the"rval was passed through ToPrimitive"outputfalse<operator returns ARC's return value,false