Patrzyłem na kod z Mozilli, który dodaje metodę filtru do Array i zawiera wiersz kodu, który mnie zdezorientował.
var len = this.length >>> 0;
Nigdy wcześniej nie widziałem >>> używanego w JavaScript.
Co to jest i co robi?
javascript
operators
bit-shift
Kenneth J.
źródło
źródło
Array.prototype.push
/Array.prototype.pop
- hexmen.com/blog/2006/12/push-and-pop (chociaż zrobił testy, haha).Odpowiedzi:
Nie tylko konwertuje nie-liczby na liczbę, ale konwertuje je na liczby, które można wyrazić jako 32-bitowe liczby całkowite bez znaku.
Chociaż obsługa JavaScript Liczby są pływaki podwójnej precyzji (*), operatory bitowe (
<<
,>>
,&
,|
i~
) są zdefiniowane w kategoriach operacji na 32-bitowych liczb całkowitych. Wykonanie operacji bitowej konwertuje liczbę na 32-bitową liczbę int ze znakiem, tracąc ułamki i bity znajdujące się wyżej niż 32, przed wykonaniem obliczeń, a następnie konwersją z powrotem na Number.Zatem wykonanie operacji bitowej bez faktycznego efektu, jak przesunięcie w prawo o 0 bitów
>>0
, jest szybkim sposobem na zaokrąglenie liczby i upewnienie się, że mieści się ona w 32-bitowym zakresie int. Dodatkowo>>>
operator potrójny , po wykonaniu operacji bez znaku, konwertuje wyniki swoich obliczeń na liczbę całkowitą bez znaku, a nie na liczbę całkowitą ze znakiem, którą robią inne, więc może być używany do konwersji liczb ujemnych na uzupełnienie do dwóch 32-bitowych wersja jako duża liczba. Użycie>>>0
zapewnia, że masz liczbę całkowitą od 0 do 0xFFFFFFFF.W tym przypadku jest to przydatne, ponieważ ECMAScript definiuje indeksy tablicy w postaci 32-bitowych liczb całkowitych bez znaku. Więc jeśli próbujesz zaimplementować
array.filter
w sposób, który dokładnie powiela to, co mówi standard ECMAScript Fifth Edition, możesz rzutować liczbę na 32-bitowy int bez znaku w ten sposób.(W rzeczywistości niewiele jest praktyczna potrzeba to mam nadzieję, że jak ludzie nie będą ustalone
array.length
na0.5
,-1
,1e21
lub'LEMONS'
. Ale to autorzy JavaScript mówimy, więc nigdy nie wiadomo ...)Podsumowanie:
(*: cóż, są zdefiniowane jako zachowujące się jak zmiennoprzecinkowe. Nie zdziwiłbym się, gdyby jakiś silnik JavaScript faktycznie używał intów, kiedy to możliwe, ze względu na wydajność. Ale to byłby szczegół implementacji, którego nie można by wziąć zaleta.)
źródło
RangeError: invalid array length
.Array.prototype.filter.call
), więc warray
rzeczywistości może nie być rzeczywistąArray
: może to być inna klasa zdefiniowana przez użytkownika. (Niestety, nie może to być niezawodnie NodeList, a wtedy naprawdę chciałbyś to zrobić, ponieważ jest to obiekt hosta. Pozostaje to jedyne miejsce, w którym realistycznie to zrobiłbyś jakoarguments
pseudo-tablica.)if
wyglądałoby w tym przypadku stwierdzenie, próbując zidentyfikować, że lewa strona oceny nie była int?'lemons'>>>0 === 0 && 0 >>>0 === 0
ocenia jako prawdziwe? chociaż cytryny to oczywiście słowo ..?Operator przesunięcia w prawo bez znaku jest używany we wszystkich implementacjach metod Mozilli w dodatkach tablicowych , aby zapewnić, że
length
właściwość jest 32-bitową liczbą całkowitą bez znaku .length
Własności przedmiotów tablicy jest opisany w opisie jako:Ten operator jest najkrótszą drogą do osiągnięcia tego celu, wewnętrznie metody tablicowe używają
ToUint32
operacji, ale ta metoda nie jest dostępna i istnieje w specyfikacji dla celów implementacji.Implementacje dodatków tablicowych Mozilli starają się być zgodne z ECMAScript 5 , spójrz na opis
Array.prototype.indexOf
metody (§ 15.4.4.14):Jak widać, chcą po prostu odtworzyć zachowanie
ToUint32
metody, aby zachować zgodność ze specyfikacją ES5 w implementacji ES3, a jak powiedziałem wcześniej, operator przesunięcia w prawo bez znaku jest najłatwiejszy.źródło
ToUint32
wydaje mi się trochę niepotrzebne.Array.prototype
metod jest celowo ogólna , można ich używać na obiektach przypominających tablice, npArray.prototype.indexOf.call({0:'foo', 1:'bar', length: 2}, 'bar') == 1;
.arguments
Obiekt jest również dobrym przykładem. W przypadku obiektów czystych tablic nie można zmienić typulength
właściwości, ponieważ implementują one specjalną metodę wewnętrzną [[Put
]], a po przypisaniu dolength
właściwości jest ponownie konwertowanaToUint32
i podejmowane są inne działania, takie jak usuwanie indeksów powyżej nowa długość ...To jest operator przesunięcia bitu w prawo bez znaku . Różnica między tym i podpisanym odpowiednim operatorem nieco zmianowej , jest to, że bez znaku prawo operator bitowy shift ( >>> ) wypełnia zerami z lewej strony, a podpisane tuż nieco operator przesunięcia ( >> ) wypełnia z bitem znaku, a tym samym zachowanie znaku wartości liczbowej po przesunięciu.
źródło
>>>
konwertuje na liczbę całkowitą, której jednoargumentowy+
nie robi.Driis wystarczająco wyjaśnił, czym jest operator i co robi. Oto znaczenie tego / dlaczego został użyty:
Przesunięcie w dowolnym kierunku o
0
zwraca oryginalną liczbę i spowoduje rzutnull
na0
. Wygląda na to, że przykładowy kod, na który patrzysz, używathis.length >>> 0
do zapewnienia, żelen
jest numeryczny, nawet jeślithis.length
nie jest zdefiniowany.Dla wielu ludzi operacje bitowe są niejasne (a Douglas Crockford / jslint sugeruje, aby nie używać takich rzeczy). Nie oznacza to, że jest to złe, ale istnieją bardziej korzystne i znane metody, które czynią kod bardziej czytelnym. Bardziej przejrzysty sposób na upewnienie się, że
len
jest0
to jedna z dwóch poniższych metod.lub
źródło
NaN
... Np.+{}
... Prawdopodobnie najlepiej połączyć te dwa rozwiązania:+length||0
>>>
jest podpisany operatora przesunięcie w prawo ( patrz str. 76 opisu JavaScript 1,5 ), w przeciwieństwie do>>
, na podpisany odpowiedni przesunięcia bitowego.>>>
zmienia wyniki przesuwania liczb ujemnych, ponieważ nie zachowuje bitu znaku podczas przesuwania . Konsekwencje tego można zrozumieć na przykładzie tłumacza:Więc to, co prawdopodobnie ma zostać tutaj zrobione, to uzyskać długość lub 0, jeśli długość jest niezdefiniowana lub nie jest liczbą całkowitą, jak w
"cabbage"
powyższym przykładzie. Myślę, że w tym przypadku można bezpiecznie założyć, żethis.length
nigdy nie będzie< 0
. Niemniej jednak argumentowałbym, że ten przykład to paskudny hack z dwóch powodów:Zachowanie w
<<<
przypadku używania liczb ujemnych, efekt uboczny prawdopodobnie nie zamierzony (lub prawdopodobnie nie wystąpi) w powyższym przykładzie.Zamiar kodu nie jest oczywisty , o czym świadczy istnienie tego pytania.
Najlepszą praktyką jest prawdopodobnie użycie czegoś bardziej czytelnego, chyba że wydajność jest absolutnie krytyczna:
źródło
-1 >>> 0
kiedykolwiek zdarzyć, a jeśli tak, to czy naprawdę pożądane jest przesunięcie go do 4294967295? Wydaje się, że spowodowałoby to uruchomienie pętli kilka razy więcej niż to konieczne.this.length
nie można tego wiedzieć. Dla każdej "rozsądnej" implementacji długość łańcucha nigdy nie powinna być ujemna, ale wtedy można by argumentować, że w "rozsądnym" środowisku możemy założyć, że istniejethis.length
właściwość, która zawsze zwraca liczbę całkowitą.Dwa powody:
Wynik >>> jest „całką”
undefined >>> 0 = 0 (ponieważ JS spróbuje wymusić LFS na kontekst numeryczny, zadziała to również dla "foo" >>> 0 itd.)
Pamiętaj, że liczby w JS mają wewnętrzną reprezentację liczby podwójnej. Jest to po prostu „szybki” sposób podstawowego zachowania rozsądku pod względem długości.
Jednak -1 >>> 0 (ups, prawdopodobnie nie jest to pożądana długość!)
źródło
Przykładowy kod Java poniżej dobrze wyjaśnia:
Wynik jest następujący:
źródło