1

有人可以向我解释什么是LeftFirst布尔标志吗?在阅读有关 [relational-operators](https://tc39.es/ecma262/#sec-relational-operators “ECMAScript 中的关系运算符定义”)和抽象关系比较的EcmaScript 规范时,我发现了类似布尔标志的东西变为或但我不知道它的用途以及它的用途可以有人清楚地解释我布尔标志的目的是什么以及为什么在规范中使用它他们给出的解释不太清楚我想知道它的用途是什么布尔标志以及为什么使用它?LeftFirsttruefalseLeftFirstleftFirst

4

2 回答 2

3

正如规范在您的链接中所说:

比较 x < y,其中 x 和 y 是值,产生真、假或未定义(这表明至少一个操作数是 NaN)。除了 x 和 y 之外,该算法还采用名为 LeftFirst 的布尔标志作为参数。该标志用于控制对 x 和 y 执行具有潜在可见副作用的操作的顺序。这是必要的,因为 ECMAScript 指定表达式的从左到右求值。LeftFirst 的默认值为 true,表示 x 参数对应于出现在 y 参数对应表达式左侧的表达式。如果 LeftFirst 为假,则情况相反,必须在 x 之前对 y 执行操作。

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

请记住,抽象关系比较只有一个版本,即用于expr1 < expr2. 没有单独的版本expr1 > expr2。调用抽象关系比较时使用该LeftFirst标志来指示应首先评估哪个表达式,以便保留从左到右的操作顺序。

这是一个例子:

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

表达式从左到右计算。obj1在 之前进行评估obj2,因此在提取基元之后,obj1小于obj2

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

obj2 > obj1实际上调用 Abstract Relational Comparison ,obj1 < obj2with LeftFirstof false。结果,右侧obj2首先被评估,因为它在源代码中排在第一位。

直观地,通过从左到右的评估,我们期望

obj2 > obj1

以导致

// evaluate obj2, increment val
2 > obj1

// evaluate obj1, increment val
2 > 3

导致false.

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

于 2020-03-21T10:08:20.800 回答
3

正如您所指出的,它是抽象关系比较算法的输入之一。它的唯一目的是确定比较算法首先将哪个操作数传递给 ToPrimitive,左边的一个 ( leftFirst = true) 还是右边的一个 ( leftFirst = false)。原因是抽象关系比较总是进行<比较,但在评估>表达式时也使用它(操作数颠倒)。因此,在处理 a 时>,需要先告诉它在右侧操作数上使用 ToPrimitive。

您可以看到它在算法的第一步中使用:

  1. 如果LeftFirst标志为true,则
    • px成为 ? ToPrimitive( x , 提示数)。
    • py成为?ToPrimitive( y , 提示数)。
  2. 否则,
    请注意:需要颠倒评估顺序以保留从左到右的评估。
    • py成为?ToPrimitive( y , 提示数)。
    • px成为 ? ToPrimitive( x , 提示数)。

同样在描述中:

该标志用于控制对xy执行具有潜在可见副作用的操作的顺序。这是必要的,因为 ECMAScript 指定表达式的从左到右求值。

例如,如果您查看and操作,该操作<>&lt;会:

  1. r为执行抽象关系比较lval < rval的结果。

它使用leftFirst的默认值,即true. 所以lval之前是通过 ToPrimitive 传递的rval

>操作确实:

  1. r为执行抽象关系比较rval < lvalLeftFirst等于false的结果。

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


在评论中你说:

非常感谢我知道为什么如果<操作员是LeftFirst true那么为什么<=也不是LeftFirst true以及为什么如果>操作员是操作员也不LeftFirst false>=LeftFirst false

这肯定有点令人困惑。<and<=不匹配(and >and>=不匹配)的原因是/<=运算>=符反转了抽象关系比较 (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;
}

您从中看到的输出是这样的:

Using > obj.lval 已评估 obj.rval 已评估 lval 已通过 ToPrimitive rval 已通过 ToPrimitive true 使用

以下是创建输出的过程>

  1. obj.lval > obj.rval表达式被评估
  2. 运行 > 算子算法
    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_ x_obj.lvaly
      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. 弧返回false
  3. 运算符反转ARC的>返回值并返回true

以下是创建后续输出的过程<

  1. obj.lval < obj.rval表达式被评估
  2. 运行 < 算子算法
    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_ x_obj.rvaly
      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. 弧返回false
  3. 运算符返回 ARC的<返回值,false
于 2020-03-21T10:06:07.803 回答