parseInt (null, 24) === 23… aspetta, cosa?


226

Bene, quindi stavo scherzando con parseInt per vedere come gestisce i valori non ancora inizializzati e mi sono imbattuto in questa gemma. Quanto segue accade per qualsiasi radix 24 o superiore.

parseInt(null, 24) === 23 // evaluates to true

L'ho testato in IE, Chrome e Firefox e tutti avvisano vero, quindi sto pensando che debba essere nelle specifiche da qualche parte. Una rapida ricerca su Google non mi ha dato alcun risultato, quindi eccomi qui, sperando che qualcuno possa spiegare.

Ricordo di aver ascoltato un discorso di Crockford in cui stava dicendo a typeof null === "object"causa di una svista che causava a Object e Null un identificatore di tipo quasi identico in memoria o qualcosa del genere, ma non riesco a trovare quel video ora.

Provalo: http://jsfiddle.net/robert/txjwP/

Modifica correzione: una radix più alta restituisce risultati diversi, 32 restituisce 785077
Modifica 2 Da zzzzBov:[24...30]:23, 31:714695, 32:785077, 33:859935, 34:939407, 35:1023631, 36:1112745


tl; dr

Spiega perché parseInt(null, 24) === 23è una vera affermazione.


49
Che strano. JavaScript ti tiene sempre aggiornato.
FishBasketGordo

1
data point: alert(parseInt(null, 34) === 23)prodottofalse
Stephen P,

1
alert(parseInt(null,26)===23);produce anche vero?!?!
Petar Ivanov,

6
[24...30]:23, 31:714695, 32:785077, 33:859935, 34:939407, 35:1023631, 36:1112745,[37...]:NaN
zzzzBov

1
Come nota aggiuntiva, undefinedpoiché il primo parametro restituisce risultati dispari per gli anni '30
zzzzBov

Risposte:


240

Si sta convertendo nullnella stringa "null"e sta provando a convertirla. Per i radix da 0 a 23, non ci sono numeri che può convertire, quindi ritorna NaN. A 24, "n"la 14a lettera viene aggiunta al sistema numerico. A 31, "u"viene aggiunta la 21a lettera e l'intera stringa può essere decodificata. A 37 in poi non esiste più alcun set numerico valido che può essere generato e viene restituito NaN.

js> parseInt(null, 36)
1112745

>>> reduce(lambda x, y: x * 36 + y, [(string.digits + string.lowercase).index(x) for x in 'null'])
1112745

3
@Tomalak: chi dice che sta usando toString()?
Ignacio Vazquez-Abrams,

3
@Ignacio. In realtà, mi sbagliavo. Non mi rendevo conto che 37 si riferiva a una radice. Mi dispiace per quello.
Mike Samuel,

3
@Robert, no, ero confuso e pensavo che stesse rivendicando qualcosa di diverso da quello che stava sostenendo. È la risposta giusta Scuse dappertutto.
Mike Samuel,

19
Penso ancora che questa risposta potrebbe fare con alcuni riferimenti. Anche se è del tutto corretto, in realtà è solo una grande affermazione ...
Lightness Races in Orbit

4
@Tomalak - controlla la mia risposta per tutti i riferimenti. Questa risposta è quella corretta (e prima), quindi penso che dovrebbe rimanere quella accettata. Anche se non fa mai male spiegare cosa succede sotto il cofano;)
David Titarenco

118

Mozilla ci dice :

La funzione parseInt converte il suo primo argomento in una stringa , lo analizza e restituisce un numero intero o NaN. Se non è NaN, il valore restituito sarà la rappresentazione intera decimale del primo argomento preso come numero nella radice specificata (base). Ad esempio, una radice di 10 indica la conversione da un numero decimale, 8 ottali, 16 esadecimali e così via. Per radici sopra 10, le lettere dell'alfabeto indicano numeri maggiori di 9. Ad esempio, per i numeri esadecimali (base 16), vengono utilizzati da A a F.

Nella specifica , 15.1.2.2/1 ci dice che la conversione in stringa viene eseguita usando il built-in ToString, che (come da 9.8) produce "null"(da non confondere toString, che produrrebbe "[object Window]"!).

Quindi, consideriamo parseInt("null", 24).

Naturalmente, questa non è una stringa numerica di base 24 nella sua interezza, ma "n" è: è 23 decimale .

Ora, l'analisi si interrompe dopo che il decimale 23 è stato estratto, perché "u" non si trova nel sistema base 24:

Se S contiene un carattere che non è una cifra radix-R, allora lascia che Z sia la sottostringa di S costituita da tutti i caratteri prima del primo carattere del genere; altrimenti, lascia che Z sia S. [15.1.2.2/11]

(E questo è il motivo per cui parseInt(null, 23)(e radici inferiori) ti dà NaNpiuttosto che 23: "n"non è nel sistema base-23.)


2
Questo è un comportamento molto tragico di parseInt (stavo pensando al motivo per cui non è stato progettato per fare eccezione in questi casi). Preferirei usare NUMBER () invece quando posso.
Grijesh Chauhan,

79

Ignacio Vazquez-Abrams è corretto, ma vediamo esattamente come funziona ...

Da 15.1.2.2 parseInt (string , radix):

Quando viene chiamata la funzione parseInt, vengono eseguite le seguenti operazioni:

  • Lascia che inputString sia ToString (stringa).
  • Sia S una sottostringa appena creata di inputString costituita dal primo carattere che non è StrWhiteSpaceChar e da tutti i caratteri che seguono quel carattere. (In altre parole, rimuovere lo spazio bianco iniziale.)
  • Lascia che il segno sia 1.
  • Se S non è vuoto e il primo carattere di S è un segno meno -, lascia che il segno sia −1.
  • Se S non è vuoto e il primo carattere di S è un segno più + o un segno meno -, quindi rimuovere il primo carattere da S.
  • Sia R = ToInt32 (radix).
  • Lascia che stripPrefix sia vero.
  • Se R ≠ 0, quindi a. Se R <2 o R> 36, restituisce NaN. b. Se R ≠ 16, lasciare stripPrefix essere falso.
  • Altrimenti, R = 0 a. Lascia R = 10.
  • Se stripPrefix è true, quindi a. Se la lunghezza di S è almeno 2 e i primi due caratteri di S sono “0x” o “0X”, quindi rimuovere i primi due caratteri da S e lasciare R = 16.
  • Se S contiene un carattere che non è una cifra radix-R, allora lascia che Z sia la sottostringa di S costituita da tutti i caratteri prima del primo carattere del genere; altrimenti, lascia che Z sia S.
  • Se Z è vuoto, restituisce NaN.
  • Sia mathInt il valore intero matematico rappresentato da Z nella notazione radix-R, usando le lettere AZ e az per le cifre con valori da 10 a 35. (Tuttavia, se R è 10 e Z contiene più di 20 cifre significative, ogni significativo una cifra dopo la ventesima può essere sostituita da una cifra 0, a scelta dell'implementazione; e se R non è 2, 4, 8, 10, 16 o 32, allora matematica può essere un'approssimazione dipendente dall'implementazione all'intero matematico valore rappresentato da Z nella notazione radix-R.)
  • Lascia che number sia il valore Number per mathInt.
  • Segno di ritorno × numero.

NOTA parseInt può interpretare solo una porzione iniziale di stringa come valore intero; ignora tutti i caratteri che non possono essere interpretati come parte della notazione di un numero intero e non viene fornita alcuna indicazione che tali caratteri siano stati ignorati.

Ci sono due parti importanti qui. Li ho messi in grassetto entrambi. Quindi, prima di tutto, dobbiamo scoprire qual è la toStringrappresentazione null. Dobbiamo guardare Table 13 — ToString Conversionsnella sezione 9.8.0 per quelle informazioni:

inserisci qui la descrizione dell'immagine

Fantastico, quindi ora sappiamo che fare toString(null)internamente produce una 'null'stringa. Fantastico, ma come gestisce esattamente le cifre (caratteri) che non sono valide all'interno della radice fornita?

Guardiamo sopra 15.1.2.2e vediamo la seguente osservazione:

Se S contiene un carattere che non è una cifra radix-R, allora lascia che Z sia la sottostringa di S costituita da tutti i caratteri prima del primo carattere del genere; altrimenti, lascia che Z sia S.

Ciò significa che gestiamo tutte le cifre PRIMA del radix specificato e ignoriamo tutto il resto.

Fondamentalmente, fare parseInt(null, 23)è la stessa cosa di parseInt('null', 23). Questo ufa sì che i due lvengano ignorati (anche se fanno parte del radix 23). Pertanto, possiamo solo analizzare n, rendendo l'intera dichiarazione sinonimo di parseInt('n', 23). :)

Ad ogni modo, ottima domanda!


33
parseInt( null, 24 ) === 23

È equivalente a

parseInt( String(null), 24 ) === 23

che equivale a

parseInt( "null", 24 ) === 23

Le cifre per la base 24 sono 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, ..., n.

Lo dice la specifica della lingua

  1. Se S contiene un carattere che non è una cifra radix-R, allora lascia che Z sia la sottostringa di S costituita da tutti i caratteri prima del primo carattere del genere; altrimenti, lascia che Z sia S.

che è la parte che garantisce che i letterali interi di tipo C come 15Lanalizzino correttamente, quindi quanto sopra è equivalente a

parseInt( "n", 24 ) === 23

"n" è la 23 ° lettera dell'elenco di cifre sopra.

QED


16

Immagino che nullvenga convertito in una stringa "null". Quindi in nrealtà è 23in "base24" (lo stesso in "base25" +), unon è valido in "base24", quindi il resto della stringa nullverrà ignorato. Ecco perché viene emesso 23fino ua quando non diventerà valido in "base31".


7

parseInt utilizza la rappresentazione alfanumerica, quindi in base-24 "n" è valido, ma "u" non è un carattere valido, quindi parseInt analizza solo il valore "n" ....

parseInt("n",24) -> 23

ad esempio, prova con questo:

alert(parseInt("3x", 24))

Il risultato sarà "3".

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.