2009-11-30 7 views
105

Ich habe Code von Mozilla angeschaut, der Array eine Filtermethode hinzufügt, und es hatte eine Codezeile, die mich verwirrte.Was ist der JavaScript >>> Operator und wie verwenden Sie ihn?

var len = this.length >>> 0; 

Ich habe noch nie >>> in JavaScript zuvor verwendet gesehen.
Was ist das und was macht es?

+0

@CMS Wahr, dieser Code/diese Frage kommt von diesen; Die Antwort hier ist jedoch spezifischer und wertvoller als die vorherigen. –

+2

Oder es ist ein Fehler oder Mozilla Jungs gehen davon aus. Länge könnte -1 sein. >>> ist ein vorzeichenloser Shift-Operator, so dass var len immer 0 oder größer ist. – user347594

+4

-1 >>> 0 === 4294967295 – jimr

Antwort

163

Es ist nicht nur konvertieren Nicht-Zahlen zu Anzahl, es wandelt sich in Zahlen, die als 32-Bit unsigned ints ausgedrückt werden können.

Obwohl JavaScript der Zahlen sind mit doppelter Genauigkeit Schwimmer (*), die Bit-Operatoren (<<, >>, &, | und ~) auf 32-Bit-Integer in Bezug auf die Operationen definiert. Bei einer bitweisen Operation wird die Zahl in einen vorzeichenbehafteten 32-Bit-Int konvertiert, wobei Brüche und höhere Stellen als 32 verloren gehen, bevor die Berechnung ausgeführt und dann wieder in Number konvertiert wird.

Eine bitweise Operation ohne tatsächlichen Effekt, wie eine Rechtsverschiebung von 0 Bits >>0, ist eine schnelle Möglichkeit, eine Zahl zu runden und sicherzustellen, dass sie im 32-Bit-int-Bereich liegt. Darüber hinaus wandelt der dreifache Operator >>> nach seiner vorzeichenlosen Operation die Ergebnisse seiner Berechnung in Zahl als vorzeichenlose Ganzzahl und nicht in die Ganzzahl mit Vorzeichen der anderen um, so dass er zur Umwandlung von Negativen in 32-Bit-Zwei verwendet werden kann. Ergänzen Sie die Version als große Zahl. Mit >>>0 wird sichergestellt, dass Sie eine Ganzzahl zwischen 0 und 0xFFFFFFFF haben.

In diesem Fall ist dies nützlich, weil ECMAScript Array-Indizes in 32-Bit-Unsigned-Ints definiert. Wenn Sie also versuchen, array.filter auf eine Weise zu implementieren, die exakt dem entspricht, was der Standard ECMAScript Fifth Edition sagt, würden Sie die Zahl auf 32-Bit unsigned int wie folgt umwandeln.

(In der Realität gibt es wenig praktische Notwendigkeit dafür, wie hoffentlich die Menschen gehen werden nicht array.length-0.5 Einstellung, -1, 1e21 oder 'LEMONS'. Aber das ist JavaScript Autoren wir reden, so dass Sie nie wissen .. .)

Zusammenfassung:

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 

(*: na ja, sie sind definiert als wie Schwimmer verhalten es würde mich nicht überraschen, wenn einige tatsächlich JavaScript-Engine ints verwendet, wenn konnte es aus Leistungsgründen.. Aber das wäre ein Implementierungsdetail y ou würde keinen Vorteil nehmen bekommen.)

+1

+2 Beschreibung und Tabelle in der Tiefe, -1 da array.length sich selbst validiert und nicht willkürlich auf irgendwas gesetzt werden kann, das keine Ganzzahl oder 0 ist (FF löst diesen Fehler aus: 'RangeError: ungültige Arraylänge'). –

+3

Die Spezifikation erlaubt jedoch bewusst, dass viele Array-Funktionen auf Nicht-Array (z. B. über 'Array.prototype.filter.call') aufgerufen werden können, so dass' Array' möglicherweise kein echtes 'Array' ist: Es könnte etwas sein andere benutzerdefinierte Klasse. (Leider kann es nicht zuverlässig eine NodeList sein, wenn Sie das wirklich wollen, da dies ein Host-Objekt ist. Das lässt den einzigen Ort, an dem Sie dies realistisch tun würden, das Pseudo-Array "arguments".) – bobince

+7

+1 für 'array.length = LEMONS'. Müssen eine wirklich nervige Person finden, damit ich diesen Trick auf ihrem Code spielen kann: 3 – yoshi

28

Das ist der unsigned right bit shift Operator. Der Unterschied zwischen diesem und den signed right bit shift operator, ist, dass der unsigned rechter Bit Verschiebungs-Operator (>>>) mit Nullen von links füllt und die unterzeichnete rechten Bit Verschiebungs-Operator (>>) mit dem füllt Vorzeichenbit, so dass das Vorzeichen des Zahlenwertes bei Verschiebung erhalten bleibt.

+2

Macht 'this.length >>> 0;' code Sinn? –

+0

Ivan, das würde es um 0 Plätze verschieben; Diese Aussage würde nichts ändern. –

+3

@Ivan, normalerweise würde ich sagen, dass die Verschiebung eines Wertes durch null Orte absolut keinen Sinn macht. Aber das ist Javascript, also könnte es eine Bedeutung dahinter haben. Ich bin kein Javascript-Guru, aber es könnte ein Weg sein, um sicherzustellen, dass der Wert tatsächlich eine ganze Zahl in der typlosen Javascript-Sprache ist. – driis

26

Driis hat ausreichend erklärt, was der Operator ist und was er tut. Hier ist der Sinn dahinter/warum es verwendet wurde:

Verschiebung in jede Richtung durch 0 nicht kehrt die ursprüngliche Zahl und wird null zu 0 gegossen. Es scheint, dass der Beispielcode, den Sie betrachten, this.length >>> 0 verwendet, um sicherzustellen, dass len numerisch ist, auch wenn this.length nicht definiert ist.

Für viele Menschen sind bitweise Operationen unklar (und Douglas Crockford/jslint schlägt vor, solche Dinge zu verwenden). Es bedeutet nicht, dass es falsch ist, aber es gibt günstigere und vertrautere Methoden, um Code lesbarer zu machen. Ein klarer Weg, um sicherzustellen, dass len ist 0 ist eine der folgenden beiden Methoden.

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

oder

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

+1 für eine schöne, klare Antwort :) – Mottie

+1

Obwohl Ihre zweite Lösung manchmal zu 'NaN' bewerten würde. '+ {}' ... Es ist wahrscheinlich am besten, die beiden zu kombinieren: '+ length || 0' – James

+1

this.length steht im Kontext des Array-Objekts, das außer einer nicht negativen Ganzzahl (mindestens in FF), so ist es hier nicht möglich. Auch {} || 1 gibt {} zurück, so dass Sie nicht besser dran sind, wenn this.length ein Objekt ist. Der Vorteil, dass diese.length-Methode in der ersten Methode auch unarisch ist, besteht darin, dass sie Fälle behandelt, in denen diese Länge NaN ist. Bearbeitete Antwort, um das zu reflektieren. –

14

>>> der unsigned rechte Shift-Operator ist (see p. 76 of the JavaScript 1.5 specification), in Bezug auf die >> Gegensatz unterzeichnete die rechten Shift-Operator.

>>> ändert die Ergebnisse der Verschiebung negativer Zahlen, weil es nicht das Vorzeichen Bit beim Verschieben bewahrt. Die Folgen davon ist, können durch Beispiel zu verstehen, von einem interpretter:

$ 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 

Also, was wohl hier sein sollte getan ist, die Länge zu erhalten, oder 0, wenn die Länge nicht definiert ist oder keine ganze Zahl ist, wie per dem "cabbage" Beispiel oben. Ich denke in diesem Fall ist es sicher anzunehmen, dass this.length niemals < 0 sein wird. Trotzdem würde ich argumentieren, dass dieses Beispiel ist ein gemeiner Hack, aus zwei Gründen:

  1. Das Verhalten von <<< wenn negativen Zahlen verwendet wird, ein Nebeneffekt vermutlich nicht beabsichtigt (oder wahrscheinlich auftritt) in der Beispiel oben.

  2. Die Absicht des Codes ist nicht offensichtlich, wie die Existenz dieser Frage bestätigt.

Am günstigsten ist es wahrscheinlich etwas besser lesbar zu verwenden, es sei denn, die Leistung absolut entscheidend ist:

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

Sooo ... @Johncatfish ist richtig? Es ist zu gewährleisten this.Length ist nicht negativ? – Anthony

+4

Könnte der Fall sein von '-1 >>> 0 'jemals geschehen, und wenn ja, ist es wirklich wünschenswert, es zu 4294967295 zu verschieben? Scheint, als würde dies dazu führen, dass die Schleife ein paar Mal mehr als nötig laufen würde. – deceze

+0

@deceze: Ohne die Implementierung von zu sehen 'this.length' ist unmöglich zu wissen.Für jede" vernünftige "Implementierung sollte die Länge eines Strings niemals negativ sein, aber dann könnte man argumentieren, dass wir in einer" gesunden "Umgebung von der Existenz einer' this.length' ausgehen können 'Eigenschaft, die immer eine ganze Zahl zurückgibt. – fmark

9

Zwei Gründe:

  1. Das Ergebnis >>> ist ein " Integral "

  2. undefined >>> 0 = 0 (Da JS die LFS numerischen Kontext versuchen und zu zwingen, wird dies für „foo“ arbeiten >>> 0, etc. sowie)

Denken Sie daran, dass die Zahlen in JS haben eine interne Repräsentation der Doppel. Es ist nur eine "schnelle" Art der grundlegenden Eingabequalität für die Länge.

Jedoch, -1 >>> 0 (oops, wahrscheinlich keine gewünschte Länge!)

53

Der vorzeichenlose Rechtsverschiebung Operator wird in den all Array Extras Methodenimplementierungen von Mozilla, um sicherzustellen, dass die length Eigenschaft ist ein unsigned 32-Bit-Integer verwendet.

Die length Eigenschaft von Array-Objekten ist described in der Beschreibung als:

Every Array object has a length property whose value is always a nonnegative integer less than 232.

Dieser Operator ist der kürzeste Weg, es zu erreichen, intern Array-Methoden verwenden, um den ToUint32 Betrieb, aber das Verfahren ist nicht zugänglich, und existieren über die Spezifikation für Implementierungszwecke.

Die Mozilla Array Extras Implementierungen versuchen ECMAScript 5 kompatibel zu sein, schauen Sie sich die Beschreibung des Array.prototype.indexOf Methode (§ 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). 
.... 

Wie Sie sehen können, sie wollen einfach nur das reproduzieren Verhalten der ToUint32 Methode, um die ES5-Spezifikation auf einer ES3-Implementierung zu erfüllen, und wie ich schon sagte, der unsigned right shift operator ist der einfachste Weg.

+0

Während die verknüpfte * array extras * Implementierung kann richtig sein (oder nahe zu korrigieren) der Code ist immer noch ein schlechtes Codebeispiel. Vielleicht würde sogar ein Kommentar zur Klärung der Absicht diese Situation lösen. – fmark

+2

Ist es möglich, dass die Länge eines Arrays * nicht * eine ganze Zahl ist? Ich kann mir das nicht vorstellen, also scheint mir diese Art von 'ToUint32' etwas unnötig zu sein. –

+6

@Marcel: Denken Sie daran, dass die meisten der 'Array.prototype' Methoden * absichtlich generisch sind *, sie können auf * array-ähnlichen * Objekten, z.B. 'Array.prototype.indexOf.call ({0: 'foo', 1: 'bar', Länge: 2}, 'bar') == 1;'. Das Argument Argumente ist ebenfalls ein gutes Beispiel. Für * pure * Array-Objekte ist es unmöglich, den Typ der 'length'-Eigenschaft zu ändern, da sie eine spezielle Methode [[' Put'] (http://bit.ly/d5hfSL)]]] implementieren und wann Eine Zuweisung wird an die 'length'-Eigenschaft vorgenommen, wiederum wird' ToUint32' konvertiert und andere Aktionen werden ausgeführt, wie das Löschen von Indizes oberhalb der neuen Länge ... – CMS