Функция больше, чем массив?
Один мой друг обнаружил какое-то интересное поведение в каком-то коде Javascript, который я решил продолжить.
Сравнение
(function (x) {return x*x;}) > [1,2,3]
возвращает true
в большинстве основных браузеров (Firefox, Chrome, Opera и Safari) и false
в IE9. Для меня нет логического результата этого сравнения, кроме undefined
, поскольку нет способа сказать, что функция больше, чем массив.
Считая это в стандарте ECMA- script, он говорит, что фактические аргументы >
, когда он используется для объектов, являются результатом вызова внутренней операции ToNumber для аргументов. Некоторые эксперименты и дальнейшее чтение говорят мне, что это не то же самое, что применять преобразование типа, например (Number) arg
. Читая спецификацию, мне трудно понять, что происходит здесь.
Может ли кто-нибудь заполнить меня тем, что на самом деле происходит здесь?
Ответы
Ответ 1
Операнды >
не обязательно преобразуются в числа. абстрактный алгоритм реляционного сравнения вызывает ToPrimitive
с подсказкой Number
, но ToPrimitive
может все еще возвращать строку (и в случае как функций, так и массивов).
Итак, вы в итоге сравниваете две строки. Результат вызова toString
в объектах функций не определяется спецификацией, хотя большинство основных движков возвращают исходный код функции (или в какой-то форме, и форматирование меняется). Результатом вызова toString
на массивах является то же самое, что join
.
Таким образом, вероятность того, что вы в конечном итоге выполните это:
"function (x) {return x*x;}" > "1,2,3"
Так как точная форма строки для функции может варьироваться от браузера к браузеру (и отметить исследования Esailija — похоже, что IE9 сохраняет внешний ()
, Chrome не работает), не удивительно, что результат может отличаться.
Ответ 2
В IE < 9, .toString
ing (function (x) {return x*x;})
дается
"(function (x) {return x*x;})"
Пока в chrome это дает:
"function (x) {return x*x;}"
Если вы сравните:
"function (x) {return x*x;}" > "1,2,3" // true
"(function (x) {return x*x;})" > "1,2,3" // false
Это фактически то же самое, что и сравнение:
"f" > "1"
"(" > "1"
То же самое, что и сравнение:
102 > 49
40 > 49
Итак, как мы получаем из сравнения функций и массивов с простым сопоставлением чисел:)
Ответ 3
Позвольте погрузиться в спецификацию ECMA. Я включил номера разделов, чтобы вы могли ссылаться.
11.8.2 Оператор больше, чем оператор ( > )
Производство RelationalExpression: RelationalExpression > Выражение Shift оценивается следующим образом:
- Пусть lref является результатом вычисления RelationalExpression.
- Пусть lval - GetValue (lref).
- Пусть rref является результатом оценки выражения Shift.
- Пусть rval - GetValue (rref).
- Пусть r является результатом выполнения абстрактного реляционного сравнения rval < lval с LeftFirst равным false. (видеть 11.8.5).
Важной частью этого является абстрактное реляционное сравнение. Определяется:
11.8.5 Алгоритм абстрактного реляционного сравнения
Функция toPrimitive
сначала будет вызываться в объектах. Хотя это смещено, чтобы вернуть Numbers, если это возможно, строки также могут быть получены. Как только это произойдет, будет рассмотрено следующее:
а. Если py является префиксом px, верните false. (Строковое значение p является префиксом значения String q, если q может быть результат конкатенации p и некоторой другой строки r. Обратите внимание, что любой Строка является префиксом сама по себе, так как r может быть пустой строкой.)
б. Если px является префиксом py, верните true.
с. Пусть k - наименьшее неотрицательное целое число такое, что символ в позиции k внутри px отличается от символа в положении k внутри py. (Должно быть такое k, поскольку ни String не является префиксом другого.)
д. Пусть m - целое число, которое является значением единицы кода для символа в позиции k в пределах px. е. Пусть n - целое число, которое является значением единицы кода для символа в позиции k внутри py. е. Если m < n, верните true. В противном случае верните false.
Это означает, что будет проверен первый символ в строке, отличной от другой. Как было указано Esailija, функция IE toString()
возвращает немного другую строку в другую, чем другие браузеры, что приводит к другому сравнению.
Это различие между браузерами кажется действительным, как указано здесь:
15.2.4.4 Object.prototype.valueOf()
Когда вызывается метод valueOf, выполняются следующие шаги:
- Пусть O - результат вызова ToObject, передающего это значение в качестве аргумента.
- Если O является результатом вызова конструктора Object с объектом-хостом (15.2.2.1), тогда a. Вернуть либо O, либо другое значение, например объект-хост изначально передан конструктору. Конкретные результат, который возвращается, определяется реализацией.
- Возврат O.
Ответ 4
Оба IE и другие браузеры будут использовать одинаковое сравнение строк для обоих объектов. Причиной разницы является то, что IE преобразует функцию в строку с буквами:
(function (x) {return x*x;})
Другие браузеры (тестирование в Firefox) выведут свою собственную скомпилированную интерпретацию функции:
function (x) {
return x * x;
}
Поскольку первый символ представления функции IE (
, который выше, чем 1
, он вернет false. Поскольку f
ниже, чем 1
, другие браузеры вернут true.