Функция больше массива? - PullRequest
43 голосов
/ 21 февраля 2012

Мой друг обнаружил интересное поведение в некотором коде Javascript, который я решил исследовать далее.

Сравнение

(function (x) {return x*x;}) > [1,2,3]

возвращает true в большинстве основных браузеров (Firefox, Chrome, Opera и Safari) и false в IE9.Для меня нет логического результата этого сравнения, кроме undefined, поскольку нет никакого способа сказать, что функция больше массива.

Если прочитать это в стандарте ECMA-скрипта,говорит, что фактические аргументы >, когда он используется на объектах, являются результатом вызова внутренней операции ToNumber для аргументов.Некоторые эксперименты и дальнейшие чтения говорят мне, что это не то же самое, что применение преобразования типа, например (Number) arg.Читая спецификацию, мне трудно понять, что здесь происходит.

Может кто-нибудь рассказать мне, что на самом деле здесь происходит?

Ответы [ 4 ]

60 голосов
/ 21 февраля 2012

В IE <9, <code>.toString ing (function (x) {return x*x;}) дает

"(function (x) {return x*x;})" 

В то время как в хроме это дает:

"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

Вот так мы получаем от сравнения функций и массивов до простого сравнения чисел:)

54 голосов
/ 21 февраля 2012

Операнды к > не обязательно преобразуются в числа. абстрактный алгоритм реляционного сравнения вызывает ToPrimitive с подсказкой Number, но ToPrimitive может по-прежнему возвращать строку (а в случае как функций, так и массивов он делает).

Итак, вы сравниваете две строки.Результат вызова toString для объектов функций не определяется спецификацией, хотя большинство основных движков возвращают исходный код функции (или ее некоторую форму, а форматирование варьируется).Результат вызова toString для массивов совпадает с join.

Таким образом, вероятность того, что вы в конечном итоге будете делать это:

"function (x) {return x*x;}" > "1,2,3"

Поскольку точная форма строки для функции может варьироваться от браузера к браузеру (и обратите внимание Исследования Esailija - похоже, IE9 сохраняет внешний (), Chrome не делает't), это не слишком удивительно, что результат может отличаться.

5 голосов
/ 21 февраля 2012

Давайте погрузимся в спецификацию ECMA. Я включил номера разделов, чтобы вы могли ссылаться.

11.8.2 Оператор большего чем (>)

Производство RelationalExpression: RelationalExpression> ShiftExpression оценивается следующим образом:

  1. Пусть lref будет результатом вычисления RelationalExpression.
  2. Пусть lval будет GetValue (lref).
  3. Пусть rref будет результатом вычисления ShiftExpression.
  4. Пусть rval будет GetValue (rref).
  5. Пусть r будет результатом выполнения абстрактного реляционного сравнения rval

Важной частью этого является Абстрактное реляционное сравнение . Который определяется:

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

Функция toPrimitive сначала будет вызываться для Объектов. Несмотря на то, что это склонно возвращать числа, если это возможно, строки также могут быть получены. Как только это произойдет, будет рассмотрено следующее:

а. Если py является префиксом px, верните false. (Строковое значение p является префиксом строкового значения q, если q может быть результат конкатенации p и некоторой другой строки r. Обратите внимание, что любой Строка сама по себе является префиксом, поскольку r может быть пустой строкой.)

б. Если px является префиксом py, верните true.

с. Пусть k будет наименьшим неотрицательным целым числом таким, что символ в позиции k в пределах px отличается от символа в позиции k в пределах py . (Должно быть такое k, поскольку ни одна строка не является префиксом другой.)

д. Пусть m будет целым числом, которое является значением единицы кода для символа в позиции k в пределах px. е. Пусть n будет целым числом, которое является значением единицы кода для символа в позиции k в пределах py. е. Если m

Это означает, что будет проверен первый символ в строке, отличный от другого. Как было отмечено Esailija, функция IE toString() возвращает немного отличную строку от других браузеров, что приводит к другому сравнению.

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

15.2.4.4 Object.prototype.valueOf ()

Когда вызывается метод valueOf, предпринимаются следующие шаги:

  1. Пусть O будет результатом вызова ToObject с передачей значения this в качестве аргумента.
  2. Если O является результатом вызова конструктора Object с хост-объектом (15.2.2.1), тогда a. Вернуть либо O, либо другое значение, например Хост-объект изначально передан в конструктор. Конкретные возвращаемый результат определяется реализацией.
  3. Возврат О.
2 голосов
/ 21 февраля 2012

И IE, и другие браузеры будут использовать одинаковое сравнение строк для обоих объектов. Причина различия заключается в том, что IE преобразует функцию в буквенную строку, как введено:

(function (x) {return x*x;})

Другие браузеры (тестирование на Firefox) выведут свою собственную скомпилированную интерпретацию функции:

function (x) {
    return x * x;
}

Поскольку первый символ представления функции IE - (, что больше 1, он вернет false. Поскольку f меньше 1, другие браузеры вернут true.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...