Что такое оператор JavaScript >>> и как вы его используете? - PullRequest
136 голосов
/ 30 ноября 2009

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

var len = this.length >>> 0;

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

Ответы [ 6 ]

190 голосов
/ 01 декабря 2009

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

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

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

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

(На самом деле в этом нет особой практической необходимости, поскольку, надеюсь, люди не будут устанавливать 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

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

56 голосов
/ 21 июня 2010

Оператор сдвига вправо без знака используется во всех реализациях метода массива extra, чтобы гарантировать, что свойство 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 <strong>ToUint32(lenValue)</strong>.
....

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

30 голосов
/ 01 декабря 2009

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

28 голосов
/ 01 декабря 2009

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

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

Для многих людей побитовые операции неясны (и Дуглас Крокфорд / Джлинт предлагает не использовать такие вещи). Это не означает, что это неправильно, но существуют более благоприятные и знакомые методы, которые делают код более читабельным. Более понятный способ убедиться, что 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; 
15 голосов
/ 21 июня 2010

>>> является беззнаковым оператором смещения вправо ( см. Стр. 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, если длина не определена или не является целым числом, как в примере "cabbage" выше. Я думаю, что в этом случае можно с уверенностью предположить, что this.length никогда не будет < 0. Тем не менее, я бы сказал, что этот пример - мерзкий взлом по двум причинам:

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

  2. Намерение кода не очевидно , как подтверждает существование этого вопроса.

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

isNaN(parseInt(foo)) ? 0 : parseInt(foo)
10 голосов
/ 21 июня 2010

Две причины:

  1. Результат >>> является "целым"

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

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

Однако , -1 >>> 0 (упс, скорее всего, не желаемая длина!)

...