Bagaimana cara saya mengatasi perilaku oktars parseInt JavaScript?

283

Coba jalankan yang berikut dalam JavaScript:

parseInt('01'); //equals 1
parseInt('02'); //equals 2
parseInt('03'); //equals 3
parseInt('04'); //equals 4
parseInt('05'); //equals 5
parseInt('06'); //equals 6
parseInt('07'); //equals 7
parseInt('08'); //equals 0 !!
parseInt('09'); //equals 0 !!

Saya baru belajar dengan susah payah bahwa JavaScript menganggap nol di depan menunjukkan integer oktal , dan karena tidak ada "8"atau "9"di basis-8, fungsi mengembalikan nol. Suka atau tidak, ini sesuai desain .

Apa solusinya?

Catatan: Demi kelengkapan, saya akan memposting solusi, tapi ini solusi yang saya benci, jadi tolong posting jawaban lain / lebih baik.


Memperbarui:

Edisi 5 dari standar JavaScript ( ECMA-262 ) memperkenalkan perubahan besar yang menghilangkan perilaku ini. Mozilla memiliki yang baik write-up .

Portman
sumber
21
Langkah 1) Bantulah diri Anda sendiri dan selalu sertakan radix sebagaimana disebutkan dalam jawaban sebelumnya serta dalam buku Doug. Langkah 2) Jika Anda serius belajar JavaScript, dapatkan sendiri buku Doug milik Anda. Itu sangat berharga. Buku favorit saya sejauh ini. Berikut ulasan fyi: realtech.burningbird.net/learning-javascript/basics/…
fuentesjr
8
Di browser yang kompatibel dengan ECMAScript 5th Edition, seperti Internet Explorer 9, parameter dasar default ke 10(desimal) kecuali angka yang diurai diawali dengan 0x, misalnya 0xFF, dalam hal ini parameter dasar default ke 16. Mudah-mudahan, suatu hari, masalah ini akan menjadi memori yang jauh.
Andy E
2
Bagaimana dengan adil +'08' === 8? Benar! Mungkin Anda benar-benar membutuhkan parseIntkode asli Anda, tetapi tidak untuk yang di atas.
kojiro
1
a) ini bukan bug, perbaiki judul b)Number('08')
OnTheFly
4
@portman: "Edisi ke-5 ... memperkenalkan perubahan besar yang menghilangkan perilaku ini" Mungkin patut menunjukkan bahwa bahkan dalam edisi ke-3 (13 tahun yang lalu), implementasi "didorong" untuk tidak melakukannya: "Ketika radix sedang 0atau undefineddan nomor string dimulai dengan 0digit yang tidak diikuti oleh xatau X, maka implementasinya dapat, dengan kebijakannya sendiri, menginterpretasikan angka tersebut sebagai oktal atau sebagai desimal. Implementasi didorong untuk menafsirkan angka dalam kasus ini sebagai desimal. " ( Penekanan saya)
TJ Crowder

Jawaban:

329

Ini adalah gotcha Javascript yang umum dengan solusi sederhana:

Cukup tentukan basisnya , atau 'radix', seperti:

parseInt('08',10); // 8

Anda juga bisa menggunakan Nomor :

Number('08'); // 8
Paolo Bergantino
sumber
57
Nomor . Mulia.
Portman
10
Angka perlu dikutip sekitar 08. Juga hanya diperingatkan, Nomor ('08 .123 ') akan menghasilkan 8.123 sebagai hasilnya. Jika Anda benar-benar menginginkan bilangan bulat, jangan gunakan Angka (atau kecocokan pola masukan Anda untuk memastikan hanya bilangan bulat).
Jason S
3
Nomor (08); memberi saya 8 di Firefox dan IE.
Paolo Bergantino
3
itu bukan bagian dari standar skrip ECMA. Saya menguji pada JSDB yang menggunakan Spidermonkey 1.7 (= mesin Firefox JS), dan mengeluh "08 bukan konstanta oktal ECMA-262 legal"
Jason S
5
Namun, gunakan '08' dalam tanda kutip. 08 tidak memenuhi standar ECMA-262 dan tidak dijamin akan berhasil tanpa peringatan dan / atau kesalahan dan / atau perilaku tertentu yang ditentukan.
Jason S
43

Jika Anda tahu nilai Anda akan berada dalam rentang integer 32 bit yang ditandatangani, maka ~~xakan melakukan hal yang benar di semua skenario.

~~"08" === 8
~~"foobar" === 0
~~(1.99) === 1
~~(-1.99)  === -1

Jika Anda mencari biner bukan ( ~), spesifikasi memerlukan konversi "ToInt32" untuk argumen yang melakukan konversi yang jelas ke Int32 dan ditentukan untuk memaksaNaN nilai menjadi nol.

Ya, ini sangat hackish tetapi sangat nyaman ...

Karl Guertin
sumber
2
Mengapa dua operasi ketika satu biner atau cukup?
Oleg V. Volkov
@Oleg, Kenapa orang cukup?
Sebastian
3
@SebastianGodelet, | 0.
Oleg V. Volkov
@Grodriguez, dan kapan 0 in | 0 adalah sebuah string?
Oleg V. Volkov
Seluruh pertanyaan ini adalah tentang alternatif parseInt untuk mengubah string menjadi integer. Jadi: selalu.
Grodriguez
24

Dari dokumentasi parseInt , gunakan argumen radix opsional untuk menentukan basis-10:

parseInt('08', 10); //equals 8
parseInt('09', 10); //equals 9

Ini menurut saya pedantic, membingungkan, dan bertele-tele (sungguh, argumen tambahan di setiap parseInt?) Jadi saya berharap ada Cara yang Lebih Baik.

Portman
sumber
6
jika Anda Tidak Suka Verbositas maka buat saja fungsi Anda sendiri yang memanggil fungsi builtin dan isi argumen yang selalu Anda pertahankan.
Jason S
3
Riiiiiiight, karena tidak seperti setiap orang di StackOverflow akan mencaci Anda untuk menulis fungsi parseIntB10. Menulis fungsi pembungkus Anda sendiri adalah ide yang buruk untuk tujuan ini.
Stefan Kendall
2
@ Iftrue: Saya pikir Anda melewatkan poin saya. Saya pribadi tidak keberatan hanya melakukan parseInt (someString, 10) di mana-mana untuk memastikan bahwa saya memaksa basis 10. OP tampaknya tidak menyukai pendekatan ini, jadi saya menyarankan alternatif, yang saya tidak akan gunakan secara pribadi tetapi mungkin itu memenuhi kebutuhan. (Ini rupanya pemikiran di balik JQuery: membuatnya nyaman dengan menambahkan kompleksitas ekstra. Saya tidak menggunakan JQuery tetapi banyak orang menganggapnya berguna.)
Jason S
4
Sebenarnya sangat penting untuk membungkus parseIntagar nyaman digunakan dalam ekspresi fungsional, seperti mappada ["7","4","09","5"].map(parseInt); Mapakan melewati indeks elemen dalam array sebagai argumen kedua, yang akan ditafsirkan parseIntsebagai basis kecuali jika Anda membungkusnya.
Plynx
11
function parseDecimal(s) { return parseInt(s, 10); }

sunting: membuat fungsi Anda sendiri, untuk melakukan apa yang benar-benar Anda inginkan, hanyalah sebuah opsi jika Anda tidak suka menambahkan panggilan ", 10" sepanjang waktu ke panggilan parseInt (). Ini memiliki kelemahan menjadi fungsi yang tidak standar: lebih nyaman bagi Anda jika Anda sering menggunakannya, tetapi mungkin lebih membingungkan bagi orang lain.

Jason S
sumber
10

Tentukan basisnya:

var number = parseInt(s, 10);
RichieHindle
sumber
Wow, kalian cepat. Saya bahkan punya jawaban di clipboard. Apakah Anda terhubung langsung ke Internet, bergaya Neo?
Portman
1
Mengapa iblis itu tidak default ke basis 10
Tom Gullen
2
Mungkin karena ini tidak terutama dimaksudkan sebagai fungsi konversi String-> Number, tetapi fungsi yang membaca Number dari basis b number String.
artistoex
2
itu default ke basis 10 tetapi memimpin nol adalah cara luas untuk menunjukkan oktal.
Nathan
7

Apakah akan sangat nakal untuk mengganti parseInt dengan versi yang mengasumsikan desimal jika tidak memiliki parameter kedua? (catatan - tidak diuji)

parseIntImpl = parseInt
parseInt = function(str, base){return parseIntImpl(str, base ? base : 10)}
Andrew Duffy
sumber
2
ya, itu akan menjadi nakal - itu akan merusak kode lain yang bergantung pada perilaku standar.
jes5199
4
Itu benar, tetapi tidak banyak. Ini juga bukan perilaku standar - dukungan oktal adalah opsional.
Andrew Duffy
2
Tetapi Anda juga menjatuhkan dukungan hex yang bukan opsional, bukan? Ini harus selalu benar:, parseInt("0xFFFFFF") === 16777215tetapi dengan hack nakal Anda di tempat itu tidak lagi berfungsiparseInt("0xFFFFFF") === 0
Timo
6

Anda juga dapat, alih-alih menggunakan parseFloat atau parseInt, gunakan operator unary ( + ).

+"01"
// => 1

+"02"
// => 2

+"03"
// => 3

+"04"
// => 4

+"05"
// => 5

+"06"
// => 6

+"07"
// => 7

+"08"
// => 8

+"09"
// => 9

dan untuk ukuran yang baik

+"09.09"
// => 9.09

MDN Link

Operator plus unary mendahului operan dan mengevaluasi operan tetapi mencoba mengubahnya menjadi angka, jika belum. Meskipun negasi unary (-) juga dapat mengkonversi non-angka, unary plus adalah cara tercepat dan lebih disukai untuk mengubah sesuatu menjadi angka , karena ia tidak melakukan operasi lain pada nomor tersebut.

SoEzPz
sumber
5

Bagaimana dengan ini untuk desimal:

('09'-0) === 9  // true

('009'-0) === 9 // true
Gordon K.
sumber
4

Jika Anda sudah melakukan banyak pengkodean dengan parseInt dan tidak ingin menambahkan ", 10" ke semuanya, Anda bisa mengganti fungsi untuk menjadikan basis 10 sebagai default:

window._oldParseInt = window.parseInt;
window.parseInt = function(str, rad) {
    if (! rad) {
        return _oldParseInt(str, 10);
    }
    return _oldParseInt(str, rad);
};

Itu mungkin membingungkan pembaca kemudian, jadi membuat fungsi parseInt10 () mungkin lebih jelas. Secara pribadi saya lebih suka menggunakan fungsi sederhana daripada harus menambahkan ", 10" sepanjang waktu - hanya menciptakan lebih banyak peluang untuk kesalahan.

bahan_15939
sumber
0

Masalah ini tidak dapat direplikasi di Chrome terbaru atau Firefox (2019).

Andrija
sumber