2009-11-30 12 views
105

Stavo guardando il codice di Mozilla che aggiungeva un metodo di filtro a Array e aveva una riga di codice che mi confondeva.Cos'è l'operatore JavaScript >>> e come lo si usa?

var len = this.length >>> 0; 

Non ho mai visto >>> utilizzato in JavaScript prima.
Che cos'è e che cosa fa?

+0

@CMS Vero, questo codice/domanda proviene da quelli; tuttavia, la risposta qui è più specifica e preziosa di quelle precedenti. –

+2

Oppure si tratta di un bug o di Mozilla che stanno assumendo questo.lunghezza potrebbe essere -1. >>> è un operatore di shift senza segno quindi var len sarà sempre 0 o maggiore. – user347594

+4

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

risposta

163

quanto non solo converte non Numeri di numero, li converte in numeri che possono essere espressi come 32 bit unsigned int.

anche se i numeri di JavaScript sono carri a precisione doppia (*), gli operatori bit per bit (<<, >>, &, | e ~) sono definiti in termini di operazioni su interi a 32 bit. Effettuando un'operazione bit per bit, il numero viene convertito in un int con segno a 32 bit, perdendo frazioni e bit di posizione superiore a 32, prima di eseguire il calcolo e quindi di tornare a Numero.

Così facendo un'operazione bit a bit senza alcun effetto reale, come uno spostamento verso destra di 0 bit >>0, è un modo rapido per arrotondare un numero e assicurarsi che sia nell'intervallo di 32 bit. Inoltre, l'operatore triplo >>>, dopo aver eseguito l'operazione senza segno, converte i risultati del suo calcolo in Numero come un numero intero senza segno anziché il numero intero con segno che gli altri fanno, quindi può essere utilizzato per convertire i negativi in ​​32-bit-two's- complemento versione come un numero grande. L'utilizzo di >>>0 assicura che tu abbia un numero intero compreso tra 0 e 0xFFFFFFFF.

In questo caso questo è utile perché ECMAScript definisce gli indici di array in termini di valori di input a 32 bit senza segno. Quindi, se si sta tentando di implementare array.filter in un modo che duplichi esattamente quello che dice lo standard ECMAScript Fifth Edition, si invierà il numero a un int non firmato a 32 bit come questo.

(In realtà c'è poco necessità pratica di questo come spero che la gente non stanno andando essere l'impostazione array.length-0.5, -1, 1e21 o 'LEMONS'. Ma questo è autori JavaScript di cui stiamo parlando, in modo da non si sa mai .. .)

Sommario:

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 

(*: beh, sono definiti come comportarsi come carri non mi sorprenderebbe se qualche motore JavaScript effettivamente utilizzato int quando potrebbe, per motivi di prestazioni.. Ma quello sarebbe un dettaglio di implementazione y ou non arriva a prendere un vantaggio di.)

+1

+2 nella descrizione di profondità e nella tabella, -1 perché array.length si convalida e non può essere impostato arbitrariamente su qualsiasi cosa che non sia un numero intero o 0 (FF genera questo errore: 'RangeError: lunghezza dell'array non valida'). –

+3

Tuttavia, la specifica permette deliberatamente numerose funzioni array da sollecitare non Array, quindi 'array' non potrebbe in realtà essere un vero' Array' (ad esempio tramite 'Array.prototype.filter.call'.): Potrebbe essere qualche altra classe definita dall'utente. (Sfortunatamente, non può essere attendibilmente un NodeList, che è quando si vorrebbe davvero farlo, dato che si tratta di un oggetto host .Questo lascia l'unica posizione in cui lo farebbe realisticamente come pseudo-Array 'arguments'.) – bobince

+7

+1 per 'array.length = LEMONS'. Devo trovare una persona davvero fastidiosa così posso giocare a questo trucco sul loro codice: 3 – yoshi

28

Questo è l'operatore unsigned right bit shift. La differenza tra questo e la signed right bit shift operator, è che l'operatore senza segno destra bit di spostamento (>>>) riempie di zeri da sinistra, e il firmato destra dell'operatore bit di spostamento (>>) riempie con la segno bit, preservando così il segno del valore numerico quando spostato.

+2

Il codice 'this.length >>> 0;' ha senso? –

+0

Ivan, che lo sposterebbe di 0 posti; quella dichiarazione non cambierebbe nulla. –

+3

@Ivan, normalmente, direi che spostare un valore di zero posti non ha assolutamente senso. Ma questo è Javascript, quindi potrebbe esserci un significato dietro. Non sono un guru di Javascript, ma potrebbe essere un modo per garantire che il valore sia in realtà un numero intero nel linguaggio Javasacript senza caratteri. – driis

26

Driis ha sufficientemente spiegato cosa è l'operatore e cosa fa. Ecco il significato dietro di esso/perché è stato utilizzato:

Shifting qualsiasi direzione da 0 non restituisce il numero originale e getterà null a 0. Sembra che il codice di esempio che stai guardando stia usando this.length >>> 0 per assicurare che len sia numerico anche se this.length non è definito.

Per molte persone, le operazioni bit a bit non sono chiare (e Douglas Crockford/jslint suggerisce di non utilizzare tali cose). Ciò non significa che sia sbagliato farlo, ma esistono metodi più favorevoli e familiari per rendere il codice più leggibile. Un modo più chiaro per garantire che len sia 0 è uno dei seguenti due metodi.

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

o

// 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 per una bella risposta chiara :) – Mottie

+1

Anche se la tua seconda soluzione a volte valuterà in 'NaN' .. E.g. '+ {}' ... Probabilmente è meglio combinare i due: '+ lunghezza || 0' – James

+1

this.length è nel contesto dell'oggetto array, che non può essere nient'altro che un numero intero non negativo (almeno in FF), quindi non è una possibilità qui. Inoltre, {} || 1 restituisce {} quindi non stai meglio se this.length è un oggetto. Il vantaggio di gettare un unendo anche questo. La lunghezza nel primo metodo è che gestisce i casi in cui questa lunghezza è NaN. Risposta modificata per riflettere questo. –

14

>>> è la unsigned giusto operatore di spostamento (see p. 76 of the JavaScript 1.5 specification), in contrapposizione al >>, il firmato giusto operatore di traslazione.

>>> modifica i risultati dello spostamento di numeri negativi perché non conserva il bit di segno quando si sposta. Le conseguenze di questo si può essere compresa con l'esempio, da un 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 

Quindi, quello che probabilmente è destinato a essere fatto qui è quello di ottenere la lunghezza, o 0 se la lunghezza non è definito o meno un intero, come per l'esempio "cabbage" sopra. Penso che in questo caso è lecito ritenere che this.length non sarà mai < 0. Tuttavia, direi che questo esempio è un brutto trucco, per due motivi:

  1. Il comportamento di <<< quando si utilizzano i numeri negativi, un effetto collaterale probabilmente non destinato (o probabile) nella esempio sopra.

  2. L'intenzione del codice non è evidente, come verifica l'esistenza di questa domanda.

Le migliori pratiche è probabilmente quello di usare qualcosa di più leggibile meno che le prestazioni è assolutamente fondamentale:

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

Sooo ... @johncatfish è corretta? E 'per garantire this.length è non negativo? – Anthony

+4

Può il caso di '-1 >>> 0' mai accadere e se è così, è davvero auspicabile per spostarlo al 4294967295 Sembra che questo potrebbe causare il loop di eseguire un paio di volte del necessario – deceze

+0

@deceze:?. Senza vedere l'attuazione di "questo.lungo" è impossibile da sapere.Per qualsiasi implementazione "sana" la lunghezza di una stringa non dovrebbe mai essere negativa, ma si potrebbe sostenere che in un ambiente "sano" possiamo assumere l'esistenza di una "lunghezza". 'proprietà che restituisce sempre un numero intero. – fmark

9

Due motivi:

  1. Il risultato di >>> è un " integrale "

  2. undefined >>> 0 = 0 (Poiché JS cercherà di costringere dell'IFL di contesto numerico, questo lavoro per "foo" >>> 0, ecc, nonché)

Ricordare che i numeri in JS hanno un interno-rappresentazione di doppio. È solo un modo "rapido" di sanità di base per la lunghezza.

Tuttavia, -1 >>> 0 (oops, probabilmente non la lunghezza desiderata!)

53

L'operatore spostamento a destra senza segno viene utilizzato nei tutti implementazioni dei metodi della supplementari matrice di Mozilla, per garantire che la struttura length è un 32 bit unsigned integer.

La length proprietà di oggetti array è described nella specifica come:

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

Questo operatore è la via più breve per realizzarlo, internamente metodi matrice utilizzare l'operazione ToUint32, ma questo metodo non è accessibile ed esistono sulla specifica per scopi di implementazione.

I Mozilla extra matrice implementazioni cercano di essere ECMAScript 5 compatibili, guardano la descrizione del metodo Array.prototype.indexOf (§ 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). 
.... 

Come si può vedere, vogliono solo per riprodurre il comportamento del metodo ToUint32 per conformarsi alle specifiche ES5 su un'implementazione ES3 e, come ho detto prima, lo unsigned right shift operator è il modo più semplice.

+0

Mentre il collegato * extra matrice * realizzazione possono essere corretti (o vicino a correggere) il codice è ancora un esempio di codice errato. Forse anche un commento per chiarire l'intenzione risolverebbe questa situazione. – fmark

+2

È possibile che la lunghezza di un array sia * non * un intero? Non riesco a immaginarlo, quindi questo tipo di 'ToUint32' mi sembra un po 'inutile. –

+6

@Marcel: Tenete a mente che la maggior parte dei metodi 'Array.prototype' sono volutamente generico * *, che può essere utilizzato su * array come oggetti * per esempio 'Array.prototype.indexOf.call ({0: 'foo', 1: 'bar', lunghezza: 2}, 'bar') == 1;'. Anche l'oggetto 'arguments' è un buon esempio. Per gli oggetti array * pure *, è impossibile cambiare il tipo di proprietà 'length', perché implementano un metodo interno [[[' Put'] (http://bit.ly/d5hfSL)]] speciale e quando viene assegnato un compito alla proprietà 'length', di nuovo viene convertito' ToUint32' e vengono intraprese altre azioni, come cancellare gli indici sopra la nuova lunghezza ... – CMS

Problemi correlati