Может ли кто-нибудь объяснить мне, что такое логический флаг "LeftFirst", который они определили в спецификации ecmaScript
Может кто-нибудь объяснить мне, что такое LeftFirst
Логический флаг? при чтении спецификации EcmaScript о [реляционных операторах] ( https://tc39.es/ecma262/"определение реляционных операторов в ECMAScript") и абстрактном реляционном сравнении я обнаружил что-то вродеLeftFirst
Boolean Flag либо становится true
или false
но я не знаю, для чего это нужно и для чего это нужно, может кто-нибудь четко объяснить мне, в чем цель LeftFirst
Логический флаг и почему он используется в спецификации, объяснение, которое они дали, не очень понятно Я хочу знать, как он используется. leftFirst
Логический флаг и почему он используется?
2 ответа
Как вы заметили, это один из входов в алгоритм абстрактного реляционного сравнения. Его единственная цель - определить, какой операнд алгоритм сравнения передается в ToPrimitive первым: левый (leftFirst = true) или правый (leftFirst = false). Причина в том, что абстрактное реляционное сравнение всегда<
сравнение, но оно также используется при оценке >
выражения (с перевернутыми операндами). Поэтому при обращении с>
, сначала нужно указать ToPrimitive в правом операнде.
Вы можете увидеть его использование на первом этапе алгоритма:
- Если флаг LeftFirst истинен, то
- Пусть будет px? ToPrimitive(x, номер подсказки).
- Пусть ру будет? ToPrimitive (y, номер подсказки).
- В противном случае,
ПРИМЕЧАНИЕ. Порядок оценки необходимо изменить на обратный, чтобы сохранить оценку слева направо.
- Пусть ру будет? ToPrimitive (y, номер подсказки).
- Пусть будет px? ToPrimitive(x, номер подсказки).
Также в описании:
Флаг используется для управления порядком, в котором операции с потенциально видимыми побочными эффектами выполняются над x и y. Это необходимо, потому что ECMAScript определяет оценку выражений слева направо.
Например, если вы посмотрите на <
а также >
операции,<
операция делает:
- Пусть r будет результатом выполнения абстрактного реляционного сравнения lval
.
При этом используется значение по умолчанию leftFirst, то естьtrue
. Такlval
передается через ToPrimitive до rval
.
Но >
операция делает:
- Пусть r будет результатом выполнения абстрактного реляционного сравнения rval < 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 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.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.а), что вызывает"lval was passed through ToPrimitive"
выходpy = ToPrimitive(y)
(Шаг 1.b), что вызывает"rval was passed through ToPrimitive"
выход
- ARC возвращается
false
- ARC получает
- Он оценивает
- В
<
оператор возвращает возвращаемое значение ARC,false
Как указано в спецификации в вашей ссылке:
Сравнение x
Флаг используется для управления порядком, в котором операции с потенциально видимыми побочными эффектами выполняются над x и y. Это необходимо, потому что ECMAScript определяет оценку выражений слева направо. Значение по умолчанию LeftFirst -true и указывает, что параметр x соответствует выражению, которое встречается слева от соответствующего выражения параметра y. Если LeftFirst ложно, то все наоборот, и операции должны выполняться над y перед 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);
Выражения оцениваются слева направо. 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
. Но это нежелательно: выражения следует оценивать слева направо.