parseInt (null, 24) === 23 ... tunggu, apa?

226

Baiklah, jadi saya bermain-main dengan parseInt untuk melihat bagaimana ia menangani nilai-nilai yang belum diinisialisasi dan saya menemukan permata ini. Di bawah ini terjadi untuk setiap radix 24 atau lebih tinggi.

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

Saya mengujinya di IE, Chrome dan Firefox dan mereka semua memberi peringatan benar, jadi saya pikir pasti ada dalam spesifikasi di suatu tempat. Pencarian Google cepat tidak memberi saya hasil apa pun jadi saya di sini, berharap seseorang dapat menjelaskan.

Saya ingat mendengarkan pidato Crockford di mana ia berkata typeof null === "object"karena kekhilafan yang menyebabkan Object dan Null memiliki pengenal tipe yang hampir sama dalam ingatan atau sesuatu di sepanjang garis itu, tetapi saya tidak dapat menemukan video itu sekarang.

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

Edit Koreksi: radix yang lebih tinggi menghasilkan hasil yang berbeda, 32 mengembalikan 785077
Edit 2 Dari zzzzBov:[24...30]:23, 31:714695, 32:785077, 33:859935, 34:939407, 35:1023631, 36:1112745


tl; dr

Jelaskan mengapa parseInt(null, 24) === 23pernyataan itu benar.

Robert
sumber
49
Unik sekali. JavaScript selalu membuat Anda siap.
FishBasketGordo
1
titik data: alert(parseInt(null, 34) === 23)diproduksifalse
Stephen P
1
alert(parseInt(null,26)===23);juga menghasilkan benar?!?!
Petar Ivanov
6
[24...30]:23, 31:714695, 32:785077, 33:859935, 34:939407, 35:1023631, 36:1112745,[37...]:NaN
zzzzBov
1
Sebagai catatan tambahan, undefinedketika parameter pertama mengembalikan hasil yang aneh untuk 30-an
zzzzBov

Jawaban:

240

Konversi nullke string "null"dan mencoba mengubahnya. Untuk radix 0 hingga 23, tidak ada angka yang dapat dikonversi, sehingga kembali NaN. Pada 24,, "n"surat ke-14, ditambahkan ke sistem angka. Pada 31,, "u"huruf ke-21, ditambahkan dan seluruh string dapat diterjemahkan. Pada 37 pada tidak ada lagi set angka yang valid yang dapat dihasilkan dan NaN dikembalikan.

js> parseInt(null, 36)
1112745

>>> reduce(lambda x, y: x * 36 + y, [(string.digits + string.lowercase).index(x) for x in 'null'])
1112745
Ignacio Vazquez-Abrams
sumber
3
@ Tomalak: Siapa bilang itu menggunakan toString()?
Ignacio Vazquez-Abrams
3
@Ignacio. Sebenarnya saya salah. Saya tidak menyadari bahwa yang saya maksud adalah radix. Maaf soal itu.
Mike Samuel
3
@ Robert, tidak, saya bingung dan mengira dia mengklaim sesuatu selain dari yang dia klaim. Itu jawaban yang tepat. Permintaan maaf di sekitar.
Mike Samuel
19
Saya masih berpikir bahwa jawaban ini dapat dilakukan dengan beberapa referensi. Meskipun sepenuhnya benar, itu benar-benar hanya satu pernyataan besar ...
Lightness Races in Orbit
4
@ Tomalak - periksa jawaban saya untuk semua referensi. Jawaban ini adalah yang benar (dan pertama) jadi saya pikir itu harus tetap diterima. Meskipun tidak ada salahnya untuk menjelaskan apa yang terjadi di bawah tenda;)
David Titarenco
118

Mozilla memberi tahu kami :

function parseInt mengonversi argumen pertamanya menjadi string , mem-parsingnya, dan mengembalikan integer atau NaN. Jika bukan NaN, nilai yang dikembalikan akan menjadi representasi bilangan bulat desimal dari argumen pertama yang diambil sebagai angka dalam radix (basis) yang ditentukan. Sebagai contoh, sebuah radix dari 10 mengindikasikan untuk mengkonversi dari angka desimal, 8 oktal, 16 heksadesimal, dan sebagainya. Untuk radio di atas 10, huruf alfabet menunjukkan angka lebih besar dari 9. Misalnya, untuk bilangan heksadesimal (basis 16), A sampai F digunakan.

Dalam spec , 15.1.2.2/1 memberi tahu kita bahwa konversi ke string dilakukan menggunakan built-in ToString, yang (sesuai 9,8) menghasilkan "null"(tidak harus bingung dengan toString, yang akan menghasilkan "[object Window]"!).

Jadi, mari kita pertimbangkan parseInt("null", 24).

Tentu saja, ini bukan string numerik basis-24 secara keseluruhan, tetapi "n" adalah: ini desimal 23 .

Sekarang, parsing berhenti setelah desimal 23 ditarik keluar, karena "u" tidak ditemukan dalam sistem basis-24:

Jika S mengandung karakter apa pun yang bukan digit radix-R, maka misalkan Z adalah substring S yang terdiri dari semua karakter sebelum karakter pertama seperti itu; jika tidak, biarkan Z menjadi S. [15.1.2.2/11]

(Dan inilah sebabnya parseInt(null, 23)(dan radex yang lebih rendah) memberi Anda NaNlebih dari 23: "n"tidak ada di sistem base-23.)

Lightness Races di Orbit
sumber
2
Ini adalah perilaku parseInt yang sangat tragis (saya berpikir mengapa itu tidak dirancang untuk pengecualian dalam kasus ini). Saya lebih suka menggunakan NUMBER () sebagai gantinya ketika saya bisa.
Grijesh Chauhan
79

Ignacio Vazquez-Abrams benar, tetapi mari kita lihat bagaimana cara kerjanya ...

Dari 15.1.2.2 parseInt (string , radix):

Ketika fungsi parseInt dipanggil, langkah-langkah berikut diambil:

  • Biarkan inputString menjadi ToString (string).
  • Biarkan S menjadi substring yang baru dibuat dari inputString yang terdiri dari karakter pertama yang bukan StrWhiteSpaceChar dan semua karakter yang mengikuti karakter itu. (Dengan kata lain, hapus spasi putih terdepan.)
  • Biarkan tanda menjadi 1.
  • Jika S tidak kosong dan karakter pertama S adalah tanda minus -, beri tanda −1.
  • Jika S tidak kosong dan karakter pertama S adalah tanda plus + atau tanda minus -, maka hapus karakter pertama dari S.
  • Misalkan R = ToInt32 (radix).
  • Biarkan stripPrefix menjadi benar.
  • Jika R ≠ 0, maka a. Jika R <2 atau R> 36, maka kembalikan NaN. b. Jika R ≠ 16, biarkan stripPrefix salah.
  • Lain, R = 0 a. Misalkan R = 10.
  • Jika stripPrefix benar, maka a. Jika panjang S setidaknya 2 dan dua karakter pertama S adalah "0x" atau "0X", maka hapus dua karakter pertama dari S dan biarkan R = 16.
  • Jika S mengandung karakter apa pun yang bukan digit radix-R, maka misalkan Z adalah substring S yang terdiri dari semua karakter sebelum karakter pertama seperti itu; jika tidak, biarkan Z menjadi S.
  • Jika Z kosong, kembalikan NaN.
  • Biarkan mathInt menjadi nilai integer matematis yang diwakili oleh Z dalam notasi radix-R, menggunakan huruf AZ dan az untuk digit dengan nilai 10 hingga 35. (Namun, jika R adalah 10 dan Z berisi lebih dari 20 digit signifikan, setiap signifikan digit setelah tanggal 20 dapat diganti dengan angka 0, pada opsi implementasi, dan jika R bukan 2, 4, 8, 10, 16, atau 32, maka mathInt mungkin merupakan pendekatan yang bergantung pada implementasi terhadap bilangan bulat matematika nilai yang diwakili oleh Z dalam notasi radix-R.)
  • Biarkan angka menjadi nilai Angka untuk mathInt.
  • Kembali tanda × angka.

CATATAN parseInt hanya dapat menginterpretasikan bagian terdepan dari string sebagai nilai integer; ia mengabaikan karakter apa pun yang tidak dapat ditafsirkan sebagai bagian dari notasi bilangan bulat, dan tidak ada indikasi yang diberikan bahwa karakter tersebut diabaikan.

Ada dua bagian penting di sini. Saya berani keduanya. Jadi pertama-tama, kita harus mencari tahu apa toStringrepresentasi nullitu. Kita perlu melihat Table 13 — ToString Conversionsdi bagian 9.8.0 untuk informasi itu:

masukkan deskripsi gambar di sini

Hebat, jadi sekarang kita tahu bahwa melakukan secara toString(null)internal menghasilkan 'null'string. Hebat, tapi bagaimana tepatnya menangani digit (karakter) yang tidak valid dalam radix yang disediakan?

Kami melihat ke atas 15.1.2.2dan kami melihat komentar berikut:

Jika S mengandung karakter apa pun yang bukan digit radix-R, maka misalkan Z adalah substring S yang terdiri dari semua karakter sebelum karakter pertama seperti itu; jika tidak, biarkan Z menjadi S.

Itu berarti bahwa kami menangani semua digit SEBELUM dengan radix yang ditentukan dan mengabaikan yang lainnya.

Pada dasarnya, melakukan parseInt(null, 23)adalah hal yang sama dengan parseInt('null', 23). The umenyebabkan dua l's untuk diabaikan (meskipun mereka ADALAH bagian dari radix 23). Karena itu, kami hanya dapat menguraikan n, menjadikan seluruh pernyataan sama dengan parseInt('n', 23). :)

Either way, pertanyaan bagus!

David Titarenco
sumber
33
parseInt( null, 24 ) === 23

Setara dengan

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

yang setara dengan

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

Digit untuk basis 24 adalah 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, ..., n.

Spesifikasi bahasa mengatakan

  1. Jika S mengandung karakter apa pun yang bukan digit radix-R, maka misalkan Z adalah substring S yang terdiri dari semua karakter sebelum karakter pertama seperti itu; jika tidak, biarkan Z menjadi S.

yang merupakan bagian yang memastikan bahwa literer integer C-style seperti 15Lparse dengan benar, sehingga di atas setara dengan

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

"n" adalah huruf ke 23 dari daftar digit di atas.

QED

Mike Samuel
sumber
16

Saya kira nullakan dikonversi ke string "null". Jadi nsebenarnya 23dalam 'base24' (sama dalam 'base25' +), utidak valid di 'base24' sehingga sisa string nullakan diabaikan. Karena itulah ia mengeluarkan 23hingga umenjadi valid di 'base31'.

Floern
sumber
7

parseInt menggunakan representasi alfanumerik, maka pada basis-24 "n" valid, tetapi "u" adalah karakter yang tidak valid, maka parseInt hanya mem-parsing nilai "n" ....

parseInt("n",24) -> 23

sebagai contoh, coba dengan ini:

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

Hasilnya akan menjadi "3".

fdaines
sumber