Как конструкция ~ [] работает в JavaScript? - PullRequest
8 голосов
/ 23 ноября 2011

Я наткнулся на рабочий код JavaScript, который не могу объяснить. Например:

  • +[]===0
  • -[]===0
  • ~[]===-1
  • ~-~[]===-2
  • ~-~-~-~-~[]===-5
  • ~-~-~-~-~[]+~[]===-6
  • ~+~[]===0
  • ~+~+~[]===-1
  • ~+~+~+~[]===0

Можете ли вы объяснить логику этих выражений?

Ответы [ 5 ]

11 голосов
/ 23 ноября 2011

[] - пустой объект массива, поэтому:

+ []: заставить пустой массив быть положительным целым числом, то есть 0, что равно от === до 0
- []: заставить пустой массив быть отрицательным целым числом, то есть 0, то есть от === до 0
~ []: побитовый НЕ пустой массив, который оценивается в -1, что составляет от === до -1
~ - ~ []: побитовое НЕ отрицательного NOTted пустого массива: ~-(-1) -> ~1 -> -2

и т.д ...

2 голосов
/ 23 ноября 2011

Вместо того, чтобы просто повторить результат, который вы продемонстрировали в этом вопросе, я попытаюсь дать описание почему вы получаете этот результат.

Объяснение

Взяв только первый пример (поскольку остальные будут делать что-то похожее), мы можем следовать спецификации, чтобы увидеть, что происходит.

Из 11.4.6 Унарный + Оператор , мы можем видеть, что происходит преобразование ToNumber.

Return ToNumber (GetValue (expr)).

С 9.3 ToNumber мы видим, что если дан объект (например, ваш массив), происходит преобразование ToPrimitive с последующей попыткой ToNumber.

Object

Примените следующие шаги:

  1. Пусть primValue будет ToPrimitive (входной аргумент, номер подсказки).
  2. Return ToNumber (primValue).

Из 9.1 В Примитив , если ToPrimitive получает Объект, мы можем видеть, что его [[DefaultValue]] выбирается:

Объект

Возвращает значение по умолчанию для объекта. Значение объекта по умолчанию извлекается путем вызова внутреннего метода [[DefaultValue]] объекта, передавая необязательную подсказку PreferredType. Поведение внутреннего метода [[DefaultValue]] определяется этой спецификацией для всех собственных объектов ECMAScript в 8.12.8.

Начиная с 8.12.8 [[DefaultValue]] (подсказка) , в конечном итоге произойдет то, что toString() будет вызываться в массиве и возвращаться. Эта строка отправляется на рекурсив ToNumber, как описано выше.

Так что же происходит, когда ToNumber преобразование выполняется для строки? Ну, это описано в 9.3.1 ToNumber, применяемый к типу строки , и немного длиннее. Проще просто сделать преобразование напрямую и посмотреть, что произойдет:

Number("");     // result is 0 on an empty string
Number("    "); // result is 0 on a string with only whitespace
Number("123");  // result is 123 on a numeric string
Number(" 123 ");// result is 123 on a numeric string with leading & trailing spaces
Number("abc");  // result is NaN (not a number) on non-numeric strings

Итак, вопрос в том, какую строку мы получаем из нашего массива. Опять же, это легко проверить.

[].toString();  // result is "" (empty string)

Поскольку результатом является пустая строка, а ToNumber преобразование пустой строки равно 0, как показано выше, это будет означать, что мы сравниваем 0 === 0.

Это было бы так же, как если бы мы сделали:

Number( [].toString() ) === 0; // true

Или нарисовать немного больше:

var x = [];
x = x.toString();  // ""
x = Number( x );   // 0
x === 0;  // true

Подробнее toString Результаты.

Чтобы показать больше toString преобразований массивов, рассмотрим следующее:

[1].toString();            // "1"
[1,2,3].toString();        // "1,2,3"
["a",1,"b",2].toString();  // "a,1,b,2"

Таким образом, чтобы выполнить преобразование ToNumber на вышеуказанных массивах, первое даст нам число, а два последних приведут к NaN.

Number([1]);            // 1
Number([1,2,3]);        // NaN
Number(["a",1,"b",2]);  // NaN

Некоторые доказательства

Чтобы еще раз подтвердить, что это преобразование toString() происходит до преобразования ToNumber, мы можем фактически изменить Array.prototype.toString, чтобы получить другой результат, и любые преобразования ToNumber будут использовать этот измененный результат.

Array.prototype.toString = function() {
    var n = 0;
    for( var i = 0; i < this.length; i++ ) {
        n += this[i];
    }
    return n;
};

Здесь я заменил toString на Array.prototype функцией, которая суммирует массив. Очевидно, вы не хотите этого делать, но мы можем показать, как теперь мы получим другой результат.

Number([1,2,3]);   // 6
+[1,2,3];          // 6

Итак, теперь вы можете видеть, что преобразование ToNumber нашего массива, которое раньше приводило к NaN, теперь приводит к сумме элементов в массиве.

2 голосов
/ 23 ноября 2011

Когда вы используете оператор + или - для чего-либо, он вызывает Number для него.Number([]) возвращает 0, поэтому вы получите первые два ответа.

Оператор ~ является побитовым НЕ.В основном это инвертирует все биты в числе, которое изменяется 0 на -1.Подробнее о побитовых операторах здесь .

Остальные - просто комбинации этих случаев.Вы можете комбинировать эти вещи, чтобы получить любое число.

1 голос
/ 23 ноября 2011

Я приложу все усилия:

[]===0, конечно, ложно, потому что [] точно не равно 0. Однако []==0 верно, потому что существует неявное приведение.

+ и -[] работают, потому что плюс или минус приводят [] к действительному числу.

~0 (битовая инверсия 0) равна -1.Таким образом, ~[]===-1 работает.

Другие работают, просто вычитая или добавляя -1 несколько раз.

0 голосов
/ 23 ноября 2011

Полагаю, поправьте меня, если я ошибаюсь, но добавление в массив (например, добавление значения в массив, а не добавление значения в массив) приводит к его преобразованию в число. Остальное просто использует базовые операторы +, - (тильда (~) - побитовое НЕ), чтобы изменить число, а затем уравнение.

So [] == array ([]);
[] + 1 == number (0);
+[]===0 (true)
...