有人能解释一下他们在 ecmaScript 规范中定义的 "LeftFirst" 布尔标志是什么吗

Can someone explain me what is the "LeftFirst" Boolean flag they have defined in the ecmaScript specification

谁能给我解释一下 LeftFirst 布尔标志是什么?在阅读 EcmaScript specification about [relational-operators](https://tc39.es/ecma262/#sec-relational-operators" relational-operators definition in ECMAScript") and Abstract Relational Comparison 时,我发现类似 LeftFirst 布尔标志的东西变成了 truefalse 但我不知道它的用途和用途有人可以清楚地向我解释 LeftFirst 布尔标志的目的是什么以及为什么在规范中使用它他们给出的解释不太清楚我想知道它的用途 leftFirst 布尔标志及其使用原因?

如您所述,它是 Abstract Relational Comparison algorithm 的输入之一。它的唯一目的是确定比较算法先将哪个操作数传递给ToPrimitive,是左边的(leftFirst = true)还是右边的(leftFirst = 假)。原因是抽象关系比较总是进行 < 比较,但在评估 > 表达式时也会使用它(操作数颠倒)。所以在处理>的时候,需要告诉它先对右边的操作数使用ToPrimitive

可以看到算法第一步用到了:

  1. If the LeftFirst flag is true, then
    • Let px be ? ToPrimitive(x, hint Number).
    • Let py be ? ToPrimitive(y, hint Number).
  2. Else,
    NOTE: The order of evaluation needs to be reversed to preserve left to right evaluation.
    • Let py be ? ToPrimitive(y, hint Number).
    • Let px be ? ToPrimitive(x, hint Number).

描述中还有:

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 < and > operations&lt; 操作会:

  1. Let r be the result of performing Abstract Relational Comparison lval < rval.

即使用leftFirst的默认值,即true。所以 lvalrval.

之前通过 ToPrimitive 传递

但是 > 操作:

  1. Let r be the result of performing Abstract Relational Comparison rval < lval with LeftFirst equal to false.

请注意它 rval < lval,而不是 lval > rval。但是它使用 leftFirst = false 因为 right 操作数在左操作数之前通过 ToPrimitive 很重要,因为真正的操作是lval > rval,所以lval应该先通过ToPrimitive传递。


您在评论中说:

Thank you very much i got to know why if < operator is LeftFirst true then why <= is also not LeftFirst true and why if > operator is LeftFirst false the >= operator is also not LeftFirst false

这确实有点令人困惑。 <<= 不匹配(以及 >>= 不匹配)的原因是 <=/>=运算符反转抽象关系比较 (ARC) 的结果。所以:

  • lval < rval 做:

    let r = ARC(lval < rval, leftFirst = true);
    return r === undefined ? false : r; // Returns what ARC returned (but
                                        // turns `undefined` into `false`)
    
  • lval <= rval

    let r = ARC(rval < lval, leftFirst = false);
    return r === undefined ? true : !r; // Returns the *inverse* of what ARC
                                        // returned (and turns `undefined`
                                        // into `true`)
    
  • lval > rval 是:

    let r = ARC(rval < lval, leftFirst = false);
    return r === undefined ? false : r; // Returns what ARC returned (but
                                        // turns `undefined` into `false`)
    
  • lval >= rval 是:

    let r = ARC(lval < rval, leftFirst = true);
    return r === undefined ? true : !r; // Returns the *inverse* of what ARC
                                        // returned (and turns `undefined`
                                        // into `true`)
    

最后一点,让我们考虑一下:

const obj = {
    get lval() {
        console.log("obj.lval was evaluated");
        return {
            valueOf() {
                console.log("lval was passed through ToPrimitive");
                return 42;
            }
        };
    },
    get rval() {
        console.log("obj.rval was evaluated");
        return {
            valueOf() {
                console.log("rval was passed through ToPrimitive");
                return 24;
            }
        };
    }
};

console.log("Using >");
const result1 = obj.lval > obj.rval;
// "obj.lval was evaluated"
// "obj.rval was evaluated"
// "lval was passed through ToPrimitive"
// "rval was passed through ToPrimitive"
console.log(result1);
// true

console.log("Using <");
const result2 = obj.lval < obj.rval;
// "obj.lval was evaluated"
// "obj.rval was evaluated"
// "lval was passed through ToPrimitive"
// "rval was passed through ToPrimitive"
console.log(result2);
// false
.as-console-wrapper {
    max-height: 100% !important;
}

你看到的输出是这样的:

使用 > obj.lval 被评价 obj.rval 被评价 lval 通过 ToPrimitive 传递 rval 通过 ToPrimitive 传递 真的 使用

以下是为 > 创建输出的结果:

  1. 计算 obj.lval > obj.rval 表达式
  2. > operator algorithm 是 运行:
    1. 它计算 lval = obj.lval(步骤 1 和 2),这会导致 "obj.lval was evaluated" 输出
    2. 它计算 rval = obj.rval(第 3 步和第 4 步),这导致 "obj.rval was evaluated" 输出
    3. 它调用 ARC(第 5 步):ARC(obj.rval < obj.lval, leftFirst = false)
      1. ARC 接收 obj.rval 作为 xobj.lval 作为 y
      2. ARC 看到 leftFirst = false 并且它确实如此:
        • py = ToPrimitive(y)(步骤2.b),导致"lval was passed through ToPrimitive"输出
        • px = ToPrimitive(x)(步骤2.c),导致"rval was passed through ToPrimitive"输出
      3. ARC returns false
  3. > 运算符反转 ARC 的 return 值和 returns true

以下是为 < 创建后续输出的情况:

  1. 计算 obj.lval < obj.rval 表达式
  2. < operator algorithm 是 运行:
    1. 它计算 lval = obj.lval(步骤 1 和 2),这会导致 "obj.lval was evaluated" 输出
    2. 它计算 rval = obj.rval(第 3 步和第 4 步),这导致 "obj.rval was evaluated" 输出
    3. 它调用 ARC(第 5 步):ARC(obj.lval < obj.rval)(leftFirst 默认为 true)
      1. ARC 接收 obj.lval 作为 xobj.rval 作为 y
      2. ARC 看到 leftFirst = true 并且它确实如此:
        • px = ToPrimitive(x)(步骤1.a),导致"lval was passed through ToPrimitive"输出
        • py = ToPrimitive(y)(步骤1.b),导致"rval was passed through ToPrimitive"输出
      3. ARC returns false
  3. <运算符returns ARC的return值,false

正如您的 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.

举个例子,考虑两个被比较的对象。要对它们执行 <,都需要首先强制转换为原语,并且允许这种原语转换有副作用,可能 影响另一个对象的转换.这将是 奇怪的 ,但它在语法上是可能的,因此规范需要指定在这种情况下必须发生什么。

请记住,只有 一个 版本的抽象关系比较,那是 expr1 < expr2 的。 expr1 > expr2 没有单独的版本。 LeftFirst 标志在调用抽象关系比较时用于指示应首先计算哪个表达式,以便保留从左到右的运算顺序。

这是一个例子:

let val = 1;
const obj1 = {
  valueOf() {
    val++;
    return val;
  }
};
const obj2 = {
  valueOf() {
    val++;
    return val;
  }
};
console.log(obj1 < obj2);

表达式是从左到右求值的。 obj1obj2 之前求值,所以提取基元后,obj1 小于 obj2.

let val = 1;
const obj1 = {
  valueOf() {
    val++;
    return val;
  }
};
const obj2 = {
  valueOf() {
    val++;
    return val;
  }
};
console.log(obj2 > obj1);

obj2 > obj1 实际上调用了抽象关系比较 obj1 < obj2,与 falseLeftFirst。结果,右侧 obj2 首先被评估,因为它在源代码中排在第一位。

直觉上,通过从左到右的评估,我们期望

obj2 > obj1

导致

// evaluate obj2, increment val
2 > obj1

// evaluate obj1, increment val
2 > 3

导致 false

如果不是这样的标志,上面的示例将导致 obj1 首先被评估,结果将是 obj1 小于 obj2 , 比较的结果是 true。但这是不可取的:表达式应该从左到右求值。