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



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

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

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

  1. 如果LeftFirst标志为,则
    • px是 ?ToPrimitive(x, hint Number).
    • py成为 ?ToPrimitive(y, hint Number)。

  2. 注意:需要颠倒评估顺序以保留从左到右的评估。
    • py成为 ?ToPrimitive(y, hint Number)。
    • px是 ?ToPrimitive(x, hint Number).

同样在描述中:

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

例如,如果查看<>操作,则&lt;操作执行以下操作:

  1. r是执行抽象关系比较的结果lval

这使用默认值 leftFirst,即true。所以lvalrval之前通过 ToPrimitive 传递。

>操作确实:

  1. r是执行抽象关系比较rval的结果,<<em>lval 与LeftFirst等于false

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


在评论中,您说:

非常感谢,我知道为什么如果<运算符是LeftFirst true那么为什么<=也不LeftFirst true,为什么如果运算符>LeftFirst false>=运算符也不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. 运行>运算符算法:
    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.rvalxobj.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. ARC 返回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.lvalxobj.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. ARC 返回false
  3. <运算符返回 ARC 的返回值,false

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

比较 x <y,>

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

对于此类内容的示例,请考虑比较两个对象。要对它们执行<,两者都需要首先被胁迫到原始对象,并且这种原始强制被允许具有可能影响另一个对象的胁迫的副作用。这会很奇怪,但它在语法上是可能的,因此规范需要指定在这种情况下必须发生的情况。

请记住,抽象关系比较只有一个版本,那就是expr1 < expr2expr1 > 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实际上调用了抽象关系比较obj1 < obj2LeftFirstfalse。因此,首先评估右侧obj2,因为它在源代码中排在第一位。

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

obj2 > obj1

以产生

// evaluate obj2, increment val
2 > obj1
// evaluate obj1, increment val
2 > 3

导致false.

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

最新更新