Может кто-нибудь объяснить мне, что такое логический флаг "LeftFirst", который они определили в спецификации ecmaScript? - PullRequest
1 голос
/ 21 марта 2020

Может кто-нибудь объяснить мне, что такое логический флаг LeftFirst? при чтении спецификаций EcmaScript о [реляционных операторах (https://tc39.es/ecma262/#sec -реляционных операторов"определение реляционных операторов в ECMAScript") и абстрактных реляционных сравнений Я нашел что-то вроде LeftFirst булева флага, он становится либо true, либо false, но я не знаю, для чего он нужен и для чего он может кто-то ясно объяснить мне, какова цель LeftFirst Логический флаг и почему он используется в спецификации, объяснение, которое они дали, не очень понятно. Я хочу знать, для чего он используется Логический флаг leftFirst и почему он используется?

Ответы [ 2 ]

3 голосов
/ 21 марта 2020

Как указано в вашей ссылке в спецификации:

Сравнение x Этот флаг используется для управления порядком, в котором операции с потенциально видимыми побочными эффектами выполняются для x и y. Это необходимо, поскольку ECMAScript определяет оценку выражений слева направо. Значение по умолчанию LeftFirst равно true и указывает, что параметр x соответствует выражению, которое встречается слева от соответствующего выражения параметра y. Если значение LeftFirst равно false, случай обратный, и операции должны выполняться над 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. Но это нежелательно: выражения должны оцениваться слева направо.

3 голосов
/ 21 марта 2020

Как вы заметили, это один из входных данных алгоритма абстрактного реляционного сравнения . Его единственная цель состоит в том, чтобы определить, какой операнд сначала передает алгоритм сравнения в ToPrimitive: один слева ( leftFirst = true) или один справа ( leftFirst = false) , Причина в том, что Абстрактное реляционное сравнение всегда выполняет сравнение <, но оно также используется при оценке выражений > (с обращенными операндами). Поэтому при обработке > необходимо сначала указать ToPrimitive для правого операнда.

Вы можете увидеть, как он используется на первом шаге алгоритма:

  1. Если флаг LeftFirst равен true , тогда
    • Пусть будет px ? ToPrimitive ( x , номер подсказки).
    • Пусть будет py ? ToPrimitive ( y , номер подсказки).
  2. Иначе,
    ПРИМЕЧАНИЕ. Порядок оценки необходимо изменить на противоположный, чтобы сохранить оценку слева направо.
    • Пусть py будет? ToPrimitive ( y , номер подсказки).
    • Пусть будет px ? ToPrimitive ( x , номер подсказки).

Также в описании:

Используется флаг контролировать порядок выполнения операций с потенциально видимыми побочными эффектами при x и y . Это необходимо, потому что ECMAScript определяет оценку выражений слева направо.

Например, если вы посмотрите на операции < и > , операцию &lt; делает:

Пусть r будет результатом выполнения абстрактного реляционного сравнения lval .

При использовании значения по умолчанию leftFirst , что составляет true. Таким образом, lval передается через ToPrimitive до rval.

Но операция > делает:

Пусть r будет результатом выполнения абстрактного реляционного сравнения rval <<em> lval с LeftFirst , равным false .

Обратите внимание, что это rval < lval, а не lval > rval. Но он использует leftFirst = false, потому что важно, чтобы операнд right был передан через ToPrimitive перед левым операндом, поскольку реальная операция - lval > rval, поэтому lval следует сначала пройти через ToPrimitive.


В комментарии вы сказали:

Большое спасибо, я узнал, почему, если оператор < равен LeftFirst true тогда почему <= также не LeftFirst true и почему, если оператор > равен LeftFirst false, оператор >= также не LeftFirst false

Это определенно немного сбивает с толку. Причина, по которой < и <= не совпадают (а > и >= не совпадают) заключается в том, что операторы <= / >= инвертируют результат абстрактного реляционного сравнения (AR *) 1284 *). Итак:

  • 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`)
    

A заключительная нота , давайте рассмотрим это:

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 был передан via 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. Он вызывает AR C (Шаг 5): ARC(obj.rval < obj.lval, leftFirst = false)
      1. AR C получает obj.rval как x и obj.lval как y
      2. AR C видит leftFirst = false и так:
        • py = ToPrimitive(y) (шаг 2.b), что приводит к выводу "lval was passed through ToPrimitive"
        • px = ToPrimitive(x) (шаг 2. c), что приводит к выводу "rval was passed through ToPrimitive"
      3. AR C возвращает false
  3. Оператор > инвертирует возвращаемое значение AR C и возвращает true

Вот что происходит для создания последующего вывода для <:

  1. Выражение obj.lval < obj.rval оценивается
  2. Алгоритм оператора < выполняется:
    1. Он оценивает lval = obj.lval (шаги 1 и 2), что вызывает "obj.lval was evaluated" output
    2. Оценивает rval = obj.rval (шаги 3 и 4), что приводит к выводу "obj.rval was evaluated"
    3. Вызывает AR C (шаг 5): ARC(obj.lval < obj.rval) (leftFirst) по умолчанию true)
      1. AR C получает obj.lval как x и obj.rval как y
      2. AR C видит leftFirst = * 1 260 * и так оно и есть:
        • px = ToPrimitive(x) (шаг 1.a), который вызывает вывод "lval was passed through ToPrimitive"
        • py = ToPrimitive(y) (шаг 1.b), который вызывает "rval was passed through ToPrimitive" output
      3. AR C возвращает false
  3. Оператор < возвращает AR C возвращаемое значение, false
...