Что такое оператор 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, вероятно, не желаемая длина!)