a.b ‹ 0, почему это не выдает ошибку, когда a.b === undefined?

Опечатка закончилась как

a.b > 0

с a.b неопределенным. Согласно MDN (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Greater_than_operator) сравнение вызывает .valueOf() для операндов. Так и должно быть

a.b.valueOf() > 0

но на самом деле это выдает ошибку, которой, похоже, не происходит. Вместо этого это, кажется, оценивается:

undefined > 0

Я что-то неправильно понимаю?

В моем случае это привело к тихой ошибке в коде Node.js. Я, конечно, хотел, чтобы вместо этого была выдана ошибка.

Могу ли я справиться с этим без явной проверки операндов?


РЕДАКТИРОВАТЬ: кажется, что freeCodeCamp описывает это более правильно, чем MDN:

https://guide.freecodecamp.org/javascript/comparison-operators/

Но я все еще думаю о самом простом способе справиться с этим, не попадая в ловушку простых опечаток.


person Leo    schedule 30.10.2019    source источник
comment
Это может вызвать ошибку, если a не определено, а вы пытаетесь получить доступ к ab, определен ли a в тот момент, когда вы пытаетесь получить доступ к ab?   -  person Alexander Staroselsky    schedule 30.10.2019
comment
@AlexanderStaroselsky Да, как я уже сказал, a.b это undefined (что означает, что a !== undefined.   -  person Leo    schedule 30.10.2019
comment
@AlexanderStaroselsky Хм, я не написал это четко в вопросе. Спасибо, что указали на это.   -  person Leo    schedule 30.10.2019
comment
сравнение вызывает .valueOf() для операндов - только если операнд является объектом. Он не делает этого для примитивных значений   -  person Bergi    schedule 30.10.2019
comment
@Bergi Спасибо, да, теперь я это понимаю, но MDN просто говорит, что каждый из этих операторов будет вызывать функцию valueOf() для каждого операнда до того, как будет выполнено сравнение. Но все же мне интересно, как люди справляются с этим. Опечатка случается легко.   -  person Leo    schedule 30.10.2019
comment
@ Лео О, вау. Я исправил MDN сейчас.   -  person Bergi    schedule 30.10.2019
comment
@ Берги Спасибо. Выглядит хорошо.   -  person Leo    schedule 30.10.2019


Ответы (1)


См. спецификацию: что он делает есть:

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

что делает, который сначала пытается принудить каждую сторону к примитиву:

а. Пусть px будет ? ToPrimitive(x, номер подсказки).

б. Пусть будет пи? ToPrimitive(y, номер подсказки).

undefined это уже примитив - ничего не выкидывает. Затем он делает:

Пусть nx будет ? ToNumeric(px).

Пусть будет? ToNumeric(py).

Приведение undefined к числу также не приводит к выбросу, но возвращает NaN:

console.log(Number(undefined));

Итак, мы добираемся до

Если Type(nx) совпадает с Type(ny), вернуть Type(nx)::lessThan(nx, ny).

где nx равно NaN, а ny равно 0. lessThan возвращает undefined, когда nx равно NaN, как описано здесь:

  1. Если x равно NaN, вернуть значение undefined.

И затем, возвращаясь к шагу после 5. в самом начале ответа:

  1. Если r не определено, вернуть false. В противном случае вернуть р.

Результат undefined, поэтому возвращается false (без выдачи ошибки).


Так и должно быть

  a.b.valueOf() > 0

Только объект будет вызываться valueOf в процессе < оценки (в a. Let px be ? ToPrimitive(x, hint Number). выше). undefined не является объектом — это уже примитив, поэтому на этом шаге не происходит никакого дополнительного преобразования.

Я, конечно, хотел, чтобы вместо этого была выдана ошибка.

Могу ли я справиться с этим без явной проверки операндов?

Не совсем, но это не должно быть сложно сделать — просто сначала проверьте, что a.b — это число, если вы не уверены. if (typeof a.b === 'number'). В этом нет ничего плохого.

person CertainPerformance    schedule 30.10.2019
comment
Спасибо, это хорошее объяснение. А это значит, что MDN слишком упрощает (в ссылке, которую я дал). Однако я также ищу безопасный способ справиться с этим. - person Leo; 30.10.2019
comment
Спасибо, поэтому я должен явно проверить операнды. - person Leo; 30.10.2019