Что такое оператор JavaScript >>> и как вы его используете?

Я смотрел код из Mozilla, который добавлял метод фильтра в Array, и у него была строка кода, которая меня смутила.

var len = this.length >>> 0;

Я никогда не видел → > , используемого в JavaScript раньше.
Что это и что он делает?

Ответ 1

Он не просто преобразует не Numbers в Number, он преобразует их в Numbers, которые могут быть выражены как 32-битные беззнаковые ints.

Хотя номера JavaScript - это float с двойной точностью (*), побитовые операторы (<<, >>, &, | и ~) определяются в терминах операций над 32-битными целыми числами, Выполнение побитовой операции преобразует число в 32-битный подписанный int, теряя любые дроби и более высокие биты, чем 32, перед выполнением вычислений и последующим преобразованием в число.

Таким образом, выполнение побитовой операции без фактического эффекта, например сдвиг вправо 0 бит >>0, является быстрым способом округления числа и обеспечения его в 32-битном диапазоне int. Кроме того, оператор triple >>> после выполнения своей неподписанной операции преобразует результаты своего вычисления в число как целое без знака, а не в целое число со знаком, которое другие делают, поэтому его можно использовать для преобразования негативов в 32- версия с двумя дополнениями как большой номер. Использование >>>0 гарантирует, что у вас есть целое число от 0 до 0xFFFFFFFF.

В этом случае это полезно, потому что ECMAScript определяет индексы Array в терминах 32-битных беззнаковых int. Поэтому, если вы пытаетесь реализовать array.filter таким образом, чтобы точно дублировать то, что говорит стандарт ECMAScript Fifth Edition, вы должны указать номер в 32-разрядный unsigned int, как это.

(На самом деле мало практической необходимости в этом, так как, надеюсь, люди не будут устанавливать array.length в 0.5, -1, 1e21 или 'LEMONS'. Но это авторы JavaScript, которые мы говорить, так что вы никогда не знаете...)

Резюме:

1>>>0            === 1
-1>>>0           === 0xFFFFFFFF          -1>>0    === -1
1.7>>>0          === 1
0x100000002>>>0  === 2
1e21>>>0         === 0xDEA00000          1e21>>0  === -0x21600000
Infinity>>>0     === 0
NaN>>>0          === 0
null>>>0         === 0
'1'>>>0          === 1
'x'>>>0          === 0
Object>>>0       === 0

(*: они определены как поведение как float. Меня не удивило бы, если бы какой-то механизм JavaScript фактически использовал ints, когда это было возможно, по соображениям производительности. Но это будет деталь реализации, которую вы бы не получили воспользоваться любым преимуществом.)

Ответ 2

Беззнаковый оператор сдвига вправо используется во всех реализациях метода extra extra в Mozilla, чтобы гарантировать, что свойство length представляет собой неподписанное 32-битное целое число.

Свойство length объектов массива описано в спецификации как:

Каждый объект Array имеет свойство length, значение которого всегда является неотрицательным целым числом меньше 2 32.

Этот оператор является самым коротким способом его достижения, методы внутри массива используют операцию ToUint32, но этот метод недоступен и существует в спецификации для целей реализации.

В реализациях экстрасети массивов Mozilla стараются быть ECMAScript 5, посмотрите описание метода Array.prototype.indexOf (& sect; 15.4.4.14 ):

1. Let O be the result of calling ToObject passing the this value 
   as the argument.
2. Let lenValue be the result of calling the [[Get]] internal method of O with 
   the argument "length".
3. Let len be ToUint32(lenValue).
....

Как вы можете видеть, они просто хотят воспроизвести поведение метода ToUint32 для соответствия спецификации ES5 в реализации ES3, и, как я сказал ранее, беззнаковый оператор сдвига вправо является самым простым способом.

Ответ 3

Это беззнаковый правый бит сдвига. Разница между этим и подписанным оператором смещения правого бита заключается в том, что оператор без знака с правом сдвига битов (→ > ) заполняется нулями слева, а оператор подписанный правый бит-сдвиг () заполняется знаковым битом, сохраняя при этом знак от численного значения при смещении.

Ответ 4

Driis достаточно объяснил, что такое оператор и что он делает. Здесь смысл, стоящий за ним/почему он использовался:

Смещение любого направления на 0 возвращает исходный номер и отбрасывает null на 0. Кажется, что пример кода, который вы смотрите, использует this.length >>> 0, чтобы гарантировать, что len является числовым, даже если this.length не определен.

Для многих людей побитовые операции неясны (и Дуглас Крокфорд /jslint предлагает не использовать такие вещи). Это не значит, что это неправильно, но существуют более благоприятные и знакомые методы, чтобы сделать код более читаемым. Более четким способом гарантировать, что len является 0, является либо один из следующих двух методов.

// Cast this.length to a number
var len = +this.length;

или

// Cast this.length to a number, or use 0 if this.length is
// NaN/undefined (evaluates to false)
var len = +this.length || 0; 

Ответ 5

>>> - это беззнаковый оператор сдвига вправо (см. стр. 76 спецификации JavaScript 1.5) в отличие от >>, подписанного оператора сдвига справа.

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

$ 1 >> 0
1
$ 0 >> 0
0
$ -1 >> 0
-1
$ 1 >>> 0
1
$ 0 >>> 0
0
$ -1 >>> 0
4294967295
$(-1 >>> 0).toString(16)
"ffffffff"
$ "cabbage" >>> 0
0

Итак, что, вероятно, должно быть сделано здесь, это получить длину, или 0, если длина undefined или не целое число, в соответствии с приведенным выше примером "cabbage". Я думаю, в этом случае можно с уверенностью предположить, что this.length никогда не будет < 0. Тем не менее, я бы сказал, что этот пример - неприятный хак по двум причинам:

  • Поведение <<< при использовании отрицательных чисел, побочный эффект, вероятно, не предназначен (или может произойти) в приведенном выше примере.

  • Намерение кода не является очевидным, как проверяет существование этого вопроса.

Лучшей практикой является, вероятно, использование чего-то более читаемого, если производительность не является абсолютно критичной:

isNaN(parseInt(foo)) ? 0 : parseInt(foo)

Ответ 6

Две причины:

  • Результат → > является "интегральным"

  • undefined → > 0 = 0 (поскольку JS будет пытаться принудить LFS к числовому контексту, это будет работать и для "foo" → > 0 и т.д.)

Помните, что числа в JS имеют внутреннее представление double. Это просто "быстрый" способ базового входного рассуждения для длины.

Однако, -1 → > 0 (oops, вероятно, не желаемая длина!)