有人可以向我解释什么是LeftFirst
布尔标志吗? 在阅读 EcmaScript 规范关于 [关系运算符](ECMAScript 中的 https://tc39.es/ecma262/#sec 关系运算符"关系运算符定义")和抽象关系比较时,我发现了类似布尔标志LeftFirst
的东西,它要么变成true
要么变成false
,但我不知道它有什么用,它有什么用,有人可以清楚地解释我LeftFirst
的目的是什么 布尔标志以及为什么在规范中使用它 他们给出的解释不太清楚我想知道它有什么用leftFirst
布尔标志以及为什么使用它?
正如你所指出的,它是抽象关系比较算法的输入之一。它的唯一目的是确定比较算法首先传递给 ToPrimitive 的操作数,左侧的操作数(leftFirst= true)或右侧的操作数(leftFirst= false)。原因是抽象关系比较总是进行<
比较,但它也用于计算>
表达式(操作数颠倒)。所以在处理>
时,需要先告诉它先在右操作数上使用ToPrimitive。
您可以看到它在算法的第一步中使用:
- 如果LeftFirst标志为真,则
- 让px是 ?ToPrimitive(x, hint Number).
- 让py成为 ?ToPrimitive(y, hint Number)。
- 还
注意:需要颠倒评估顺序以保留从左到右的评估。
- 让py成为 ?ToPrimitive(y, hint Number)。
- 让px是 ?ToPrimitive(x, hint Number).
同样在描述中:
该标志用于控制对x和y执行具有潜在可见副作用的操作的顺序。这是必要的,因为 ECMAScript 指定了表达式从左到右的计算。
例如,如果查看<
和>
操作,则<
操作执行以下操作:
- 设r是执行抽象关系比较的结果lval
。
这使用默认值 leftFirst,即true
。所以lval
在rval
之前通过 ToPrimitive 传递。
但>
操作确实:
- 设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 传递 真 用以下是为>
创建该输出时发生的情况:
- 计算
obj.lval > obj.rval
表达式 - 运行
>
运算符算法:- 它评估
lval = obj.lval
(步骤1和2),这会导致"obj.lval was evaluated"
输出 - 它评估
rval = obj.rval
(步骤3和4),这会导致"obj.rval was evaluated"
输出 - 它调用 ARC(步骤 5):
ARC(obj.rval < obj.lval, leftFirst = false)
- ARC接受
obj.rval
x
,obj.lval
y
- ARC 看到 leftFirst =
false
,因此它确实如此:py = ToPrimitive(y)
(步骤 2.b),导致"lval was passed through ToPrimitive"
输出px = ToPrimitive(x)
(步骤 2.c),导致"rval was passed through ToPrimitive"
输出
- ARC 返回
false
- ARC接受
- 它评估
>
运算符反转 ARC 的返回值并返回true
以下是为<
创建后续输出时发生的情况:
- 计算
obj.lval < obj.rval
表达式 - 运行
<
运算符算法:- 它评估
lval = obj.lval
(步骤 1 和 2),这会导致"obj.lval was evaluated"
输出 - 它评估
rval = obj.rval
(步骤3和4),这会导致"obj.rval was evaluated"
输出 - 它调用 ARC(步骤 5):
ARC(obj.lval < obj.rval)
(leftFirst 默认为 true)- ARC接受
obj.lval
x
和obj.rval
y
- ARC 看到 leftFirst =
true
,所以它确实如此:px = ToPrimitive(x)
(步骤 1.a),导致"lval was passed through ToPrimitive"
输出py = ToPrimitive(y)
(步骤 1.b),导致"rval was passed through ToPrimitive"
输出
- ARC 返回
false
- ARC接受
- 它评估
<
运算符返回 ARC 的返回值,false
正如规范在您的链接中所说:
比较 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
实际上调用了抽象关系比较obj1 < obj2
,LeftFirst
false
。因此,首先评估右侧obj2
,因为它在源代码中排在第一位。
直观地说,通过从左到右的评估,我们期望
obj2 > obj1
以产生
// evaluate obj2, increment val
2 > obj1
// evaluate obj1, increment val
2 > 3
导致false
.
如果不是这样的标志,上面的示例将导致首先评估obj1
,结果将是obj1
将小于obj2
,并且比较的结果将是true
。但这是不可取的:表达式应该从左到右计算。