Неявное преобразование типа данных в JavaScript при сравнении целого с строкой с использованием ==

Код:

var num = 20;

if(num == "20")
{
    alert("It works");
}
else
{
    alert("Not working");
}

Вопрос:

  • В программировании на языке С у нас есть продвижение типа данных типа имени правила, где, когда существует смешанный тип данных (пример: добавление целого числа и с плавающей запятой), целое число сначала преобразуется в плавающую точку до того, как добавление будет выполнить.

  • Приведенный выше код подскажет мне окно с сообщением "It works", которое показывает, что условие проверки if равно true.

  • Для слабо типизированного JavaScript мне просто интересно: существует ли какое-либо правило, такое как C, которое определяет, какое преобразование будет выполняться в какой ситуации? Кроме того, приведенный выше код JavaScript преобразует значение переменной num из целочисленного значения в строковое значение перед выполнением сравнения или наоборот?

Ответ 1

Да, все правила преобразования типов, применяемые оператором equals, описаны в спецификации ECMA-262 в Алгоритм сравнения абстрактного равенства.

Алгоритм может выглядеть довольно сложным, но его можно обобщить на следующие случаи:

  • Тип двух операндов одинаковый:

    • Для примитивов (String, Number, Boolean, Null, Undefined)
      • Возвращает true, если значение точно такое же
    • Для типа объекта
      • Возвращает true, если две ссылки указывают на один и тот же объект
  • Если типы двух операндов отличаются

    • Если тип одного операнда имеет значение Null или Undefined
      • Возвращает true, только если другое значение операнда равно null или undefined
    • Если один из операндов имеет тип Boolean или Number
      • (после некоторых шагов) Преобразуйте другой операнд в число и сравните
  • Если один из операндов - это Object, а другой - примитив

    • Выполните преобразование объектов в примитив в объект и сравните его снова

Преобразование Object-to-Primitive выполняется с помощью абстрактной операции ToPrimitive, этот метод попытается преобразовать объект в примитивное значение, используя внутренний метод [[PrimitiveValue]].

Это попытается выполнить эжектирование методов valueOf и toString, и оно примет значение первого, возвращающее примитивное значение.

В случае, если эти два метода не возвращают примитив, или они не подлежат вызову, генерируется TypeError, например:

1 == { toString:null } // TypeError!

Вышеприведенный оператор создает TypeError, потому что метод по умолчанию Object.prototype.valueOf не делает ничего больше, чем фактически тот же экземпляр объекта (this, а не примитивное значение), и мы устанавливаем собственный toString свойство, которое не является функцией.

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

Ответ 2

В JavaScript есть два оператора, которые могут использоваться для сравнения двух значений: операторы == и ===.

Цитируется из JavaScript Полное руководство 6-го издания:

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

и

Строгий оператор равенства === оценивает свои операнды, а затем сравнивает   два значения, как показано ниже, без преобразования типа.

Поэтому я предлагаю вам использовать === все время, чтобы избежать таких проблем, как:

null == undefined // These two values are treated as equal. 
"0" == 0 // String converts to a number before comparing. 
0 == false // Boolean converts to number before comparing. 
"0" == false // Both operands convert to numbers before comparing.

P.S. Я мог бы опубликовать все "сравнительные рекомендации", как написано в книге, но слишком долго;) Просто скажите мне, и я отредактирую свой пост для вас.

Ответ 3

Избегайте неявного преобразования типов в JavaScript. Всегда выполняйте шаги для тестирования и/или преобразования отдельных значений перед их сравнением, чтобы убедиться, что вы сравниваете яблоки с яблоками. Всегда проверяйте явно для undefined, чтобы определить, имеет ли значение или свойство значение, используйте значение null, чтобы указать, что переменные или свойства объекта не относятся к какому-либо объекту, а также преобразовывать и сравнивать все другие значения, чтобы гарантировать выполнение операций с значениями тот же тип.

Ответ 4

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

Код:

var values = ["123",
          undefined,
          "not a number",
          "123.45",
          "1234 error",
          "",
          "       ",
          null,
          undefined,
          true,
          false,
          "true",
          "false"
          ];

for (var i = 0; i < values.length; i++){
    var x = values[i];
    console.log("Start");
    console.log(x);
    console.log(" Number(x) = " + Number(x));
    console.log(" parseInt(x, 10) = " + parseInt(x, 10));
    console.log(" parseFloat(x) = " + parseFloat(x));
    console.log(" +x = " + +x);
    console.log(" !!x = " + !!x);
    console.log("End");
}

Выход:

"Start"
"123"
" Number(x) = 123"
" parseInt(x, 10) = 123"
" parseFloat(x) = 123"
" +x = 123"
" !!x = true"
"End"

"Start"
undefined
" Number(x) = NaN"
" parseInt(x, 10) = NaN"
" parseFloat(x) = NaN"
" +x = NaN"
" !!x = false"
"End"

"Start"
"not a number"
" Number(x) = NaN"
" parseInt(x, 10) = NaN"
" parseFloat(x) = NaN"
" +x = NaN"
" !!x = true"
"End"

"Start"
"123.45"
" Number(x) = 123.45"
" parseInt(x, 10) = 123"
" parseFloat(x) = 123.45"
" +x = 123.45"
" !!x = true"
"End"

"Start"
"1234 error"
" Number(x) = NaN"
" parseInt(x, 10) = 1234"
" parseFloat(x) = 1234"
" +x = NaN"
" !!x = true"
"End"

"Start"
""
" Number(x) = 0"
" parseInt(x, 10) = NaN"
" parseFloat(x) = NaN"
" +x = 0"
" !!x = false"
"End"

"Start"
"       "
" Number(x) = 0"
" parseInt(x, 10) = NaN"
" parseFloat(x) = NaN"
" +x = 0"
" !!x = true"
"End"

"Start"
null
" Number(x) = 0"
" parseInt(x, 10) = NaN"
" parseFloat(x) = NaN"
" +x = 0"
" !!x = false"
"End"

"Start"
undefined
" Number(x) = NaN"
" parseInt(x, 10) = NaN"
" parseFloat(x) = NaN"
" +x = NaN"
" !!x = false"
"End"

"Start"
true
" Number(x) = 1"
" parseInt(x, 10) = NaN"
" parseFloat(x) = NaN"
" +x = 1"
" !!x = true"
"End"

"Start"
false
" Number(x) = 0"
" parseInt(x, 10) = NaN"
" parseFloat(x) = NaN"
" +x = 0"
" !!x = false"
"End"

"Start"
"true"
" Number(x) = NaN"
" parseInt(x, 10) = NaN"
" parseFloat(x) = NaN"
" +x = NaN"
" !!x = true"
"End"

"Start"
"false"
" Number(x) = NaN"
" parseInt(x, 10) = NaN"
" parseFloat(x) = NaN"
" +x = NaN"
" !!x = true"
"End"

Ответ 5

Лучше использовать код ниже для понимания неявного преобразования.

var values = [ 0 , 123, "0", "123", -0, +0, NaN, +NaN, -NaN, false, true, "false", "true", null, undefined, "null", "undefined", "", "GoodString", "  "];

for (var i = 0; i < values.length; i++){
    console.log("<<<<<<<<<<<<Starting comparing:  " + i + ">>>>>>>>>>>>>>>");
    for (var j = 0; j < values.length; j++){
		console.log(values[i],`==`, values[j]);
		console.log(eval(values[i] == values[j]));
	}
}