Рассказ о преобразовании типов

Когда дело доходит до оператора равенства, бывает сложно понять, как JavaScript обрабатывает преобразование типов. Лучшее понимание - или, по крайней мере, наличие ссылки, к которой можно вернуться - может положительно повлиять на ваше кодирование и лучше подготовить вас к загадкам и непреднамеренным побочным эффектам преобразования типов.

Мы надеемся, что эта статья станет тем справочником как для вас, так и для меня. Начнем с определений!

Строгое равенство

Строгое равенство (===, часто == на других языках) знакомо большинству программистов. Он проверяет, идентичны ли два значения и одного и того же типа.

Операнд

Объект / значение, которое мы используем в операции. В 5 == '5' есть два операнда - 5 и '5'.

Преобразование типов

Преобразование типов - это перенос данных из одного типа данных в другой. Это может происходить неявно (когда компилятор автоматически назначает типы данных) или явно (когда ваш код сообщает JavaScript, что нужно выполнить преобразование). Когда преобразование происходит автоматически, как в случае с оператором равенства, мы также называем этот процесс приведением типов. Давайте посмотрим на это в действии.

Оператор равенства

Оператор равенства (==) выполняет преобразование типов при сравнении операндов. Это может быть полезно при сравнении значений, которые выглядят одинаково, но относятся к разным типам.

Одним из таких случаев является проверка чисел по строкам, которые выглядят как числа.

5 == '5'; // true

Отчасти понятно, что здесь происходит. Строка конвертируется в число, затем проверяется строгое равенство… верно? Может быть! Может, число переводится в строку? В этом посте я приведу конкретные примеры и описание вопросов, связанных с преобразованием типов.

Спецификация ECMAScript может сбивать с толку. Он почти читается как исследовательская работа по математике. Когда я хотел получить более подробную информацию об операторе равенства, я обратился к источнику. Ниже я построю простой протокол, который поможет вам определить, как абстрактное равенство работает в нескольких сценариях.

ЗАМЕТКА:

Некоторые шаги будут иметь явный возврат (например, # 2):
- return true

Другие вместо этого будут выводить новую операцию абстрактного равенства (например, # 3):
- 5 == 5;

Результирующие операнды возвращаются на шаг № 1, где алгоритм повторяется до тех пор, пока не будет достигнут явный возврат.

Абстрактный алгоритм сравнения равенства

Обновлено для ECMA2020.

For x == y, выполните следующие действия:

1. Если x и y относятся к одному и тому же типу , верните x === y

5 === 5;                             => true
'foo' === 'bar';                     => false
false === false;                     => true
null === undefined;                  => false
{ foo: 'bar' } === { foo: 'bar' };   => false

2. Если x равно null, а y равно undefined (или наоборот), вернуть true

null == undefined; => true

3. Если x - строка, а y - число (или наоборот), преобразовать строку в число

'5' == 5;
  => convert '5' to Number
  => Number('5') => 5
5 == 5;

4. Если x - это BigInt, а y - это строка (или наоборот), преобразуйте строку в BigInt

BigInt(5) == '5';
  => convert '5' to BigInt
  => Bigint('5')
BigInt(5) == BigInt('5');

5. Если x или y является логическим, преобразуйте его в число

true == 3;
  => convert true to Number
  => Number(true) => 1
1 == 3;
false == 'foo';
  => convert false to Number
  => Number(false) => 0
0 == 'foo';

6. Если x является строкой, числом, BigInt или символом, а y является объектом (или наоборот), попытайтесь преобразовать объект в примитив.

Иногда объекты имеют встроенные / предопределенные методы, которые помогают с преобразованием в примитивы. Такие методы выходят за рамки этой статьи, и мы проигнорируем эти частные случаи - мы будем использовать простой объект
{ foo: 'bar' } для этого шага.

’baz’ == { foo: 'bar' };
5 == { foo: 'bar' };
BigInt(5) == { foo: 'bar' };
Symbol('baz') == { foo: 'bar' };

В каждом из этих случаев для преобразования используется toString метод объекта. По умолчанию этот метод просто возвращает строку '[object Object]'.

[String/Number/BigInt/Symbol] == { foo: 'bar' };
  => convert object using .toString()
  => { foo: 'bar' }.toString() => '[object Object]'
[String/Number/BigInt/Symbol] == '[object Object]';

7. Если x является BigInt, а y - числом (или наоборот):

  • Если x или y равно NaN, +∞ или -∞, вернуть false
BigInt(5) == NaN; => false
BigInt(5) == Infinity; => false
BigInt(5) == -Infinity; => false
  • Если ни x, ни y не равно NaN, +∞ или -∞, заполните Строгое равенство для математических значений x и y
BigInt(5) == 5;
  => Number(BigInt(5)) => 5
5 === 5; => true
BigInt(5) == 3;
  => Number(BigInt(5)) => 5
5 === 3; => false

8. Если вы дойдете до конца этого контрольного списка, верните false

Наконец, мы можем дать окончательный ответ на наш первоначальный вопрос!

5 == '5';
  => Step #3
    => If x is a String and y is a Number (or vice-versa),
    => convert the String to a Number
Number('5') => 5
// return to top of protocol
5 == 5;
  => Step #1
    => If x and y are of the same type, return x === y
5 === 5; => true

Теперь мы подтвердили, что в этом примере наша строка '5' преобразована в число, затем было проверено строгое равенство с использованием результата.

Заключение

Большой! Надеюсь, это руководство послужит полезным справочником для ваших нужд оператора равенства.

В JavaScript есть несколько других операторов, которые инициируют преобразование типов. В следующий раз я исследую их в другом пошаговом руководстве. Я хотел бы услышать ваш отзыв, поэтому оставьте, пожалуйста, отзыв! Это поможет мне в будущих статьях.

Источники