Чистая функция дает строго равные аргументы, дающие не строго равные результаты

Ниже чистая функция f, для которой f(a) !== f(b), несмотря на a === b (обратите внимание на строгие равенства) для некоторых значений a и b:

var f = function (x) {
   return 1 / x;
}

+0 === -0 // true
f(+0) === f(-0) // false

Существование таких функций может привести к труднодоступным ошибкам. Есть ли другие примеры, которые я должен утомлять?

Ответ 1

Да, потому что NaN !== NaN.

var f = function (x) { return Infinity - x; }

Infinity === Infinity // true
f(Infinity) === f(Infinity) // false

f(Infinity) // NaN

Некоторые другие примеры, которые дают NaN, аргументы которых могут быть строго равны:

0/0
Infinity/Infinity
Infinity*0
Math.sqrt(-1)
Math.log(-1)
Math.asin(-2)

Ответ 2

это поведение вполне нормально, потому что в математической теории -0 === +0 истинно, а 1/(-0) === 1/(+0) нет, потому что -inf != +inf

EDIT: хотя я действительно удивлен тем, что javascript действительно может обрабатывать эти виды математических понятий.

EDIT2: кроме того, описанное вами явление полностью основано на том факте, что вы разделите на нуль, из которого вы должны ожидать хотя бы какого-то странного поведения.

Ответ 3

1/+0 - бесконечность и 1/-0 -Infinity, а +0 === -0.

Это можно объяснить тем, что ECMA определяет -0 равным +0 как особый случай, тогда как в других операциях эти два значения сохраняют свои разные свойства, что приводит к некоторым несоответствиям.

Это возможно только потому, что язык явно определяет два не равных значения равными, которые на самом деле не являются.

Другие примеры, если таковые имеются, должны основываться на одном и том же искусственном равенстве и даны http://ecma262-5.com/ELS5_HTML.htm#Section_11.9.6, нет другого такого исключения, поэтому, вероятно, нет другого примера этого.

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

var f = function(x) {
   return 1 / (x + 0);
}
f(+0) === f(-0)

Ответ 4

В ECMAScript 3 еще один пример, где === ведет себя удивительно, - это объединенные функции. Рассмотрим такой случай:

function createConstantFunction(result) {
    return function () {
        return result;
    };
}

var oneReturner = createConstantFunction(1);  // a function that always returns 1
var twoReturner = createConstantFunction(2);  // a function that always returns 2

Реализация разрешена для "объединения" двух функций (см. раздел 13.2 спецификации), и если это так, то oneReturner === twoReturner будет true (см. раздел 13.1.2), хотя две функции выполняют разные вещи. Аналогично этим:

// a perfect forwarder: returns a sort of "duplicate" of its argument
function duplicateFunction(f) {
    return function (f) {
        return f.apply(this, arguments);
    };
}

var myAlert = duplicateFunction(alert);
console.myLog = duplicateFunction(console.log);

Здесь реализация может сказать, что myAlert === console.myLog, хотя myAlert фактически эквивалентен alert, а console.myLog фактически эквивалентен console.log.

(Однако этот аспект ECMAScript 3 не был сохранен в ECMAScript 5: функции больше не допускаются к объединению.)

Ответ 5

Я не уверен, что это так страшно;-) Javascript - это не чистый язык и наличие +/- 0, а равенство -0 и +0 специфичны для IEEE-754 и "хорошо определены", даже если, возможно, иногда это удивительно. (Даже NaN!= NaN, всегда являющееся истинным, хорошо определено, например.)

От подписанный нуль:

В соответствии со стандартом IEEE 754 отрицательный нуль и положительный ноль должны сравниваться как с обычными (численными) операторами сравнения...

Технически, поскольку два входа в f различны, тогда результат также может быть другим. Для чего он стоит, Haskell будет относить 0 == -0 как истинный, но будет относиться к (1 / 0) == (1 / (-0)) как false.

Однако я нахожу этот интересный вопрос.

Счастливое кодирование.

Ответ 6

Существует много таких функций, вот еще один пример

function f (a) {
  return a + 1;
}

1 == "1" но f (1)!= f ( "1" )

Это потому, что равенство - это тонкая концепция.

Возможно, более страшно, чем в вашем примере -0 === +0.