Побитовые операции со строками в JavaScript

в javascript следующий тест символьных двоичных операций печатает 0 676 раз:

var s = 'abcdefghijklmnopqrstuvwxyz';
var i, j;
for(i=0; i<s.length;i++){ for(j=0; j<s.length;j++){ console.log(s[i] | s[j]) }};

если js использовал фактическое двоичное представление строк, я ожидал бы здесь некоторых ненулевых значений.

аналогично, тестирование двоичных операций над строками и целыми числами, следующая печать 26 255s и 0s, соответственно. (255 было выбрано, потому что это 11111111 в двоичном).

var s = 'abcdefghijklmnopqrstuvwxyz';
var i; for(i=0; i<s.length;i++){ console.log(s[i] | 255) }
var i; for(i=0; i<s.length;i++){ console.log(s[i] & 255) }

что здесь делает javascript? Похоже, javascript бросает любую строку в false перед бинарные операции.

Примечания

если вы попробуете это в Python, он выдает ошибку:

>>> s = 'abcdefghijklmnopqrstuvwxyz'
>>> [c1 | c2 for c2 in s for c1 in s]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for |: 'str' and 'str'

но такие вещи, как это кажется работа в php.

3 ответов


в JavaScript, когда строка используется с двоичным оператором, она сначала преобразуется в число. Соответствующие части спецификации ECMAScript показаны ниже, чтобы объяснить, как это работает.

побитовые операторы:

производство A : A @ B, где @ - один из побитовых операторов в производствах выше, оценивается следующим образом:

  1. пусть lref будет результатом оценки A.
  2. пусть lval будет GetValue (lref).
  3. пусть rref будет результатом оценки B.
  4. пусть rval будет GetValue (rref).
  5. пусть lnum будет ToInt32 (lval).
  6. пусть rnum будет ToInt32 (rval).
  7. возвращает результат применения побитового оператора @ к lnum и rnum. В результате получается 32-разрядное целое число со знаком.

ToInt32:

абстрактная операция ToInt32 преобразует аргумент к одному из 232 целочисленные значения в диапазоне от -231 до 231-1 включительно. Эта абстрактная операция функционирует следующим образом:

  1. пусть number является результатом вызова ToNumber во входном аргументе.
  2. если число NaN, +0, -0,+∞, или -∞, возврат +0.
  3. пусть posInt-знак (номер) * пол (abs (номер)).
  4. пусть int32bit будет posInt по модулю 232; то есть, конечное целое значение k Введите число с положительным знаком и менее 232 по величине такой, что математическая разница posInt и k математически является целым числом, кратным 232.
  5. если int32bit больше или равно 231, возврат int32bit-232, иначе возвращает int32bit.

внутренняя функция ToNumber вернет NaN для любой строки, которая не может быть проанализирована как число, и ToInt32 (NaN) даст 0. Таким образом, в вашем примере кода все побитовые операторы с буквами в качестве операндов будут оцениваться как 0 | 0, что объясняет, почему печатается только 0.

обратите внимание, что что-то вроде '7' | '8' будет оценено как 7 | 8 потому что в этом случае строки, используемые в качестве операндов могут быть успешно объединить в число.

что касается того, почему поведение в Python отличается, на самом деле нет никакого неявного преобразования типов в Python, поэтому ожидается ошибка любой тип, который не реализует двоичные операторы (с помощью __or__, __and__, etc.), и строки не реализуют эти двоичные операторы.

Perl делает что-то совершенно другое, побитовые операторы реализованы для строк и он будет по существу выполнять побитовый оператор для соответствующих байтов из каждой строки.

если вы хотите использовать JavaScript и получить тот же результат, что и Perl, вам нужно сначала преобразовать символы в их кодовые точки с помощью str.charCodeAt, выполните побитовый оператор на результирующих целых числах, а затем используйте String.fromCodePoint для преобразования результирующих числовых значений в символы.


Я был бы удивлен, если бы JavaScript работал вообще с побитовыми операциями над нечисловыми строками и производил что-либо значимое. Я бы предположил, что, поскольку любой побитовый оператор в JavaScript преобразует свой операнд в 32-битное целое число, он просто превратит все нечисловые строки в 0.

Я бы использовал...

"a".charCodeAt(0) & 0xFF

производит 97, код ASCII для "a", который является правильным, учитывая, что он замаскирован байтом со всеми битами набор.

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


JavaScript использует принуждение типа, которое позволяет ему пытаться автоматически анализировать строки как числа при попытке выполнить над ними числовую операцию. Проанализированное значение равно 0 или более вероятно NaN. Это, очевидно, не даст вам информацию, которую вы пытаетесь получить.

Я думаю, что вы ищете -charCodeAt что позволит вам получить числовое значение Unicode для символа в строке и, возможно, дополнительный fromCodePoint, который преобразует числовое значение символа.