parseInt (null, 24) === 23 chờ đợi, cái gì?


226

Được rồi, vì vậy tôi đã tìm hiểu về parseInt để xem cách nó xử lý các giá trị chưa được khởi tạo và tôi tình cờ phát hiện ra viên ngọc này. Điều dưới đây xảy ra cho bất kỳ cơ số 24 trở lên.

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

Tôi đã thử nghiệm nó trong IE, Chrome và Firefox và tất cả đều cảnh báo đúng, vì vậy tôi nghĩ nó phải nằm trong đặc tả ở đâu đó. Một tìm kiếm nhanh trên Google đã không cho tôi bất kỳ kết quả nào vì vậy tôi đang ở đây, hy vọng ai đó có thể giải thích.

Tôi nhớ đã nghe một bài phát biểu của Crockford nơi anh ấy nói typeof null === "object"vì sự giám sát khiến Object và Null có một định danh loại gần giống nhau trong bộ nhớ hoặc một cái gì đó dọc theo những dòng đó, nhưng bây giờ tôi không thể tìm thấy video đó.

Hãy dùng thử: http://jsfiddle.net/robert/txjwP/

Chỉnh sửa Chỉnh sửa: cơ số cao hơn trả về các kết quả khác nhau, 32 trả về 785077
Chỉnh sửa 2 Từ zzzzBov:[24...30]:23, 31:714695, 32:785077, 33:859935, 34:939407, 35:1023631, 36:1112745


tl; dr

Giải thích tại sao parseInt(null, 24) === 23là một tuyên bố đúng.


49
Thật kỳ quặc làm sao. JavaScript luôn giúp bạn luôn tự tin.
FishBasketGordo

1
điểm dữ liệu: alert(parseInt(null, 34) === 23)được sản xuấtfalse
Stephen P

1
alert(parseInt(null,26)===23);còn sản xuất đúng không?!?!
Petar Ivanov

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

1
Như một lưu ý bổ sung, undefinedvì tham số đầu tiên trả về kết quả kỳ lạ trong 30
giây

Câu trả lời:


240

Nó đang chuyển đổi nullthành chuỗi "null"và cố gắng chuyển đổi nó. Đối với các cơ số từ 0 đến 23, không có chữ số nào nó có thể chuyển đổi, vì vậy nó trả về NaN. Ở tuổi 24, "n"chữ cái thứ 14, được thêm vào hệ thống chữ số. Ở tuổi 31, "u"chữ cái thứ 21, được thêm vào và toàn bộ chuỗi có thể được giải mã. Ở tuổi 37, không còn bộ số hợp lệ nào có thể được tạo và NaN được trả về.

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: Ai nói rằng nó đang sử dụng toString()?
Ignacio Vazquez-Abrams

3
@Ignacio. Thật ra, tôi đã sai. Tôi đã không nhận ra 37 đang đề cập đến một cơ số. Xin lỗi vì điều đó.
Mike Samuel

3
@Robert, không, tôi đã bối rối và nghĩ rằng anh ta đang yêu cầu một cái gì đó khác với những gì anh ta đang yêu cầu. Đó là câu trả lời đúng. Xin lỗi tất cả xung quanh.
Mike Samuel

19
Tôi vẫn nghĩ rằng câu trả lời này có thể làm với một số tài liệu tham khảo. Mặc dù nó hoàn toàn chính xác, nó thực sự chỉ là một khẳng định lớn ...
Các cuộc đua nhẹ nhàng trong quỹ đạo

4
@Tomalak - kiểm tra câu trả lời của tôi cho tất cả các tài liệu tham khảo. Câu trả lời này là câu trả lời đúng (và đầu tiên) vì vậy tôi nghĩ nó vẫn nên được chấp nhận. Mặc dù không bao giờ đau lòng để giải thích những gì diễn ra dưới mui xe;)
David Titarenco

118

Mozilla nói với chúng tôi :

Hàm parseInt chuyển đổi đối số đầu tiên của nó thành một chuỗi , phân tích cú pháp và trả về một số nguyên hoặc NaN. Nếu không phải là NaN, giá trị được trả về sẽ là biểu diễn số nguyên thập phân của đối số đầu tiên được lấy dưới dạng số trong cơ số (cơ sở) đã chỉ định. Ví dụ, một cơ số 10 chỉ ra để chuyển đổi từ một số thập phân, 8 bát phân, 16 thập lục phân, v.v. Đối với các radice trên 10, các chữ cái của bảng chữ cái biểu thị các chữ số lớn hơn 9. Ví dụ, đối với các số thập lục phân (cơ số 16), A đến F được sử dụng.

Trong thông số kỹ thuật , 15.1.2.2/1 cho chúng ta biết rằng việc chuyển đổi thành chuỗi được thực hiện bằng cách sử dụng tích hợp ToString, theo (theo ngày 9,8) mang lại "null"(không bị nhầm lẫn toString, sẽ mang lại kết quả "[object Window]"!).

Vì vậy, hãy xem xét parseInt("null", 24).

Tất nhiên, đây không phải là toàn bộ chuỗi số cơ sở 24, nhưng "n" là: nó là số thập phân 23 .

Bây giờ, phân tích cú pháp dừng sau khi rút ra số thập phân 23, vì "u" không tìm thấy trong hệ thống cơ sở 24:

Nếu S chứa bất kỳ ký tự nào không phải là chữ số radix-R, thì hãy để Z là chuỗi con của S bao gồm tất cả các ký tự trước ký tự đầu tiên như vậy; mặt khác, đặt Z là S. [15.1.2.2/11]

(Và đây là lý do tại sao parseInt(null, 23)(và các radice thấp hơn) cung cấp cho bạn NaNchứ không phải 23: "n"không có trong hệ thống cơ sở 23.)


2
Đây là hành vi rất bi thảm của parseInt (Tôi đã suy nghĩ tại sao nó không được thiết kế mặc dù ngoại lệ trong những trường hợp này). Tôi muốn sử dụng SỐ () thay vì khi tôi có thể.
Grijesh Chauhan

79

Ignacio Vazquez-Abrams là chính xác, nhưng hãy xem chính xác cách thức hoạt động của nó ...

Từ 15.1.2.2 parseInt (string , radix):

Khi hàm parseInt được gọi, các bước sau được thực hiện:

  • Đặt inputString là ToString (chuỗi).
  • Đặt S là một chuỗi con mới được tạo của inputString bao gồm ký tự đầu tiên không phải là StrWhiteSpaceChar và tất cả các ký tự theo sau ký tự đó. (Nói cách khác, loại bỏ khoảng trắng hàng đầu.)
  • Đặt ký hiệu là 1.
  • Nếu S không trống và ký tự đầu tiên của S là dấu trừ -, hãy đặt dấu là −1.
  • Nếu S không trống và ký tự đầu tiên của S là dấu cộng + hoặc dấu trừ - thì xóa ký tự đầu tiên khỏi S.
  • Đặt R = ToInt32 (cơ số).
  • Hãy để dải phân cách là đúng.
  • Nếu R 0, thì a. Nếu R <2 hoặc R> 36, thì trả về NaN. b. Nếu R ≠ 16, hãy để dảiPrefix là sai.
  • Khác, R = 0 a. Đặt R = 10.
  • Nếu dảiPrefix là đúng, thì a. Nếu độ dài của S ít nhất là 2 và hai ký tự đầu tiên của S là hoặc 0x, hoặc 0 0, thì xóa hai ký tự đầu tiên khỏi S và để R = 16.
  • Nếu S chứa bất kỳ ký tự nào không phải là chữ số radix-R, thì hãy để Z là chuỗi con của S bao gồm tất cả các ký tự trước ký tự đầu tiên như vậy; mặt khác, đặt Z là S.
  • Nếu Z trống, trả về NaN.
  • Đặt mathInt là giá trị nguyên toán học được biểu thị bằng Z theo ký hiệu radix-R, sử dụng các chữ cái AZ và az cho các chữ số có giá trị 10 đến 35. (Tuy nhiên, nếu R là 10 và Z chứa hơn 20 chữ số có nghĩa chữ số sau số 20 có thể được thay thế bằng 0 chữ số, tùy chọn triển khai và nếu R không phải là 2, 4, 8, 10, 16 hoặc 32, thì mathInt có thể là xấp xỉ phụ thuộc vào thực hiện với số nguyên toán học giá trị được biểu thị bằng Z trong ký hiệu radix-R.)
  • Đặt số là giá trị Số cho mathInt.
  • Dấu trả về × số.

LƯU Ý parseInt chỉ có thể hiểu một phần đầu của chuỗi là một giá trị nguyên; nó bỏ qua bất kỳ ký tự nào không thể được hiểu là một phần của ký hiệu số nguyên và không có dấu hiệu nào cho thấy bất kỳ ký tự nào như vậy bị bỏ qua.

Có hai phần quan trọng ở đây. Tôi in đậm cả hai. Vì vậy, trước hết, chúng ta phải tìm ra những gì toStringđại diện nulllà. Chúng ta cần xem Table 13 — ToString Conversionstrong phần 9.8.0 để biết thông tin đó:

nhập mô tả hình ảnh ở đây

Tuyệt vời, vì vậy bây giờ chúng tôi biết rằng làm toString(null)nội bộ mang lại một 'null'chuỗi. Tuyệt vời, nhưng chính xác thì nó xử lý các chữ số (ký tự) không hợp lệ trong cơ số được cung cấp như thế nào?

Chúng tôi nhìn ở trên 15.1.2.2và chúng tôi thấy nhận xét sau:

Nếu S chứa bất kỳ ký tự nào không phải là chữ số radix-R, thì hãy để Z là chuỗi con của S bao gồm tất cả các ký tự trước ký tự đầu tiên như vậy; mặt khác, đặt Z là S.

Điều đó có nghĩa là chúng tôi xử lý tất cả các chữ số TRƯỚC cho cơ số được chỉ định và bỏ qua mọi thứ khác.

Về cơ bản, làm parseInt(null, 23)là điều tương tự như parseInt('null', 23). Các unguyên nhân hai llà để được bỏ qua (mặc dù họ là một phần của cơ số 23). Do đó, chúng tôi chỉ có thể phân tích cú pháp n, làm cho toàn bộ tuyên bố đồng nghĩa với parseInt('n', 23). :)

Dù bằng cách nào, câu hỏi tuyệt vời!


33
parseInt( null, 24 ) === 23

Tương đương với

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

tương đương với

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

Các chữ số cho cơ sở 24 là 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, ..., n.

Thông số ngôn ngữ nói

  1. Nếu S chứa bất kỳ ký tự nào không phải là chữ số radix-R, thì hãy để Z là chuỗi con của S bao gồm tất cả các ký tự trước ký tự đầu tiên như vậy; mặt khác, đặt Z là S.

đó là phần đảm bảo rằng các số nguyên kiểu chữ C giống như 15Lphân tích cú pháp đúng, vì vậy phần trên tương đương với

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

"n" là chữ cái thứ 23 của danh sách chữ số ở trên.

QED


16

Tôi đoán nullđược chuyển đổi thành một chuỗi "null". Vì vậy, nthực sự là 23trong 'base24' (tương tự trong 'base25' +), ukhông hợp lệ trong 'base24' nên phần còn lại của chuỗi nullsẽ bị bỏ qua. Đó là lý do tại sao nó xuất ra 23cho đến khi utrở thành hợp lệ trong 'base31'.


7

parseInt sử dụng biểu diễn chữ và số, sau đó trong cơ sở 24 "n" là hợp lệ, nhưng "u" là ký tự không hợp lệ, sau đó parseInt chỉ phân tích giá trị "n" ....

parseInt("n",24) -> 23

làm ví dụ, thử với điều này:

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

Kết quả sẽ là "3".

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.