Học bằng cách đọc theo thứ tự

Chuyển Đổi Kiểu — Number / String / parseInt và Chuyển Đổi Ngầm

Học chuyển đổi kiểu tường minh và chuyển đổi ngầm, kèm cách kiểm tra kiểu bằng typeof và phát hiện NaN.

Một số lượng gõ vào ô nhập, hay một số tiền chôn trong dữ liệu từ nguồn bên ngoài. Dù chúng trông giống nhau trên màn hình — cùng là 1200 — việc JavaScript đang giữ nó dưới dạng chuỗi hay số sẽ thay đổi kết quả một phép tính làm với nó.

Bài này nói về hai thứ: chuyển đổi kiểu tường minh (explicit conversion) (tự bạn thay đổi kiểu của một giá trị bằng cách gọi thứ như Number), và chuyển đổi kiểu ngầm (implicit conversion) (toán tử tự động căn chỉnh kiểu trước khi tính toán).

Kiểm Tra Kiểu Hiện Tại — typeof

Căn chỉnh kiểu bắt đầu từ việc biết một thứ đang là kiểu gì. Các lỗi như tổng tiền sai, hay so sánh không ra kết quả như bạn mong đợi, thường bắt nguồn từ một giá trị bạn tưởng là số nhưng hóa ra là chuỗi.

Trước tiên, hãy nói về cách kiểm tra kiểu của một giá trị ngay tại chỗ.

Đặt typeof (toán tử trả về tên kiểu của một giá trị dưới dạng chuỗi) trước một giá trị, và bạn nhận được kiểu của nó. typeof 1200"number", và typeof "1200""string".

Cái trả về không phải bản thân kiểu — mà là một chuỗi đặt tên kiểu — nên khi dùng nó trong kiểm tra, so sánh với một chuỗi có nháy, như typeof price === "number".

typeof Trả Về Gì
typeof "A-1042"Trả về tên kiểudưới dạng chuỗi"string"typeof 1200Kiểm tra bằng=== "number""number"typeof trueKhông phải chữbare word"boolean"
Cái trả về không phải bản thân kiểu — mà là một chuỗi đặt tên kiểu. So sánh nó với một chuỗi có nháy khi kiểm tra.
const orderId = "A-1042";   // mã đơn hàng

console.log(typeof orderId);   // string
console.log(typeof 500);       // number
console.log(typeof true);      // boolean

// Hoạt động cả trên kết quả biểu thức, không chỉ biến
console.log(typeof (500 + 100));            // number
console.log(typeof (500 > 100));            // boolean
console.log(typeof `Order ${orderId}`);      // string

Kiểm tra kiểu của 3 giá trị đến từ màn hình sửa sản phẩm. inputPrice / stockCount / isPublished đã được khai báo sẵn.

① Hiển thị kiểu của inputPrice.

② Hiển thị kiểu của stockCount.

③ Hiển thị kiểu của isPublished.

④ Kiểm tra và hiển thị inputPrice có KHÔNG phải kiểu number hay không.

(Nếu chạy đúng, phần giải thích sẽ xuất hiện.)

JavaScript / TypeScript Editor

Chạy code để xem đầu ra

Chuyển Đổi Ngầm — Chỉ + Thành Nối Chuỗi

Tính toán với các giá trị khác kiểu, và JavaScript không dừng lại với lỗi. Toán tử căn chỉnh kiểu trước, rồi cứ tiếp tục.

Hành vi bạn đã thấy == làm trước khi so sánh cũng xuất hiện ở các toán tử số học. Vì nó không bao giờ dừng lại, bạn sẽ không nhận ra sự không khớp cho đến khi thấy kết quả.

Điểm bất ngờ là + có cách xử lý khác các toán tử còn lại. + chọn nối chuỗi nếu một trong hai bên là chuỗi. Bên là số được chuyển thành chuỗi, nên "3" + 1 thành "31".

- * / %, ngược lại, không có ý nghĩa nào để nối chuỗi lại, nên chúng chuyển bên chuỗi thành số rồi tính toán.

Nếu một ô tổng tiền hiện hai số nằm cạnh nhau, đó là dấu hiệu chúng đã được cộng trước khi chuyển đổi.

Toán Tử Khác Nhau Chuyển Sang Kiểu Khác Nhau
"3" + 1Ghép thànhmột chuỗi"31""3" - 1Chuyển thànhsố trước2"3" * 2Chuyển thànhsố trước6
Chỉ + ưu tiên nối chuỗi. Các toán tử số học khác chuyển bên chuỗi thành số.
const inputQuantity = "3";   // giá trị từ ô nhập số lượng (chuỗi)

// Chỉ hiển thị không phân biệt được số 31 với chuỗi "31" — dùng typeof bên dưới
console.log(inputQuantity + 1);   // 31   ghép thành chuỗi "31"
console.log(inputQuantity - 1);   // 2    chuyển thành số trước khi trừ
console.log(inputQuantity * 2);   // 6    nhân cũng chuyển đổi
console.log(inputQuantity / 3);   // 1    chia cũng chuyển đổi

// typeof xác nhận chỉ kết quả của + có kiểu khác
console.log(typeof (inputQuantity + 1));   // string
console.log(typeof (inputQuantity - 1));   // number

Coi Giá Trị Từ Bên Ngoài Là Chuỗi

Số đến từ ô nhập, hay chôn trong dữ liệu từ nguồn bên ngoài, thường đến dưới dạng chuỗi. Nếu phép cộng bạn định là 1200 + 300 lại hiện thành "1200300" nằm cạnh nhau, đó là dấu hiệu một bên vẫn còn là chuỗi. Chuyển nó thành số trước khi cộng.

Chuyển Đổi Tường Minh — Number / String / Boolean

Để việc chuyển đổi cho toán tử tự lo nghĩa là hướng nó chuyển đổi là cố định theo từng toán tử, nên ý định của bạn không hiện ra trong code. Tự căn chỉnh kiểu trước một phép tính, và kết quả sẽ dễ theo dõi hơn.

JavaScript cung cấp một hàm chuyển đổi cho mỗi kiểu.

Number(value) chuyển thành số, String(value) thành chuỗi, và Boolean(value) thành boolean — mỗi hàm trả về một giá trị mới. Biến gốc giữ nguyên không đổi.

Boolean theo đúng quy tắc falsy: ""0 thành false, và một chuỗi không rỗng thành true. Lưu ý Number("") trả về 0, không phải NaN, nên chuyển một ô trống thẳng thành số coi nó như số lượng 0.

3 Hàm Chuyển Đổi Trả Về Gì
Number("1200")chuỗi → số1200String(1200)số → chuỗi"1200"Boolean("")chuỗi → booleanfalse
Cả ba đều trả về một giá trị mới — biến bạn truyền vào giữ nguyên.
const inputQuantity = "3";   // giá trị từ ô nhập số lượng
const shippingFee = 500;     // phí vận chuyển (số)

console.log(Number(inputQuantity));         // 3     chuỗi thành số
console.log(Number(inputQuantity) + 1);     // 4     phép cộng giờ đã hoạt động như số
console.log(String(shippingFee));           // 500   số thành chuỗi
console.log(typeof String(shippingFee));    // string

console.log(Boolean("Hanoi"));   // true    một chuỗi không rỗng
console.log(Boolean(""));        // false   chuỗi rỗng là falsy
console.log(Number(""));         // 0       chuỗi rỗng thành 0

// Biến gốc vẫn là chuỗi ngay cả sau khi chuyển đổi
console.log(typeof inputQuantity);   // string

Tính thành tiền giỏ hàng từ số lượng đến từ ô nhập và một đơn giá. inputQuantity / unitPrice / inputCoupon đã được khai báo sẵn.

① Hiển thị kết quả chuyển inputQuantity thành số.

② Tính và hiển thị thành tiền từ số ở ① và unitPrice.

③ Hiển thị kiểu bạn nhận được khi chuyển thành tiền ở ② thành chuỗi.

④ Hiển thị kết quả chuyển ô mã giảm giá trống, inputCoupon, thành boolean.

JavaScript / TypeScript Editor

Chạy code để xem đầu ra

Chuyển Đổi Thất Bại — NaN và Number.isNaN

Truyền cho Number một chuỗi không thể biến thành số, và JavaScript không ném ra lỗi. Thay vào đó nó trả về NaN (giá trị nghĩa là kết quả không thể trở thành số) và cứ tiếp tục sang phép tính kế tiếp.

Đó là lý do một ô số tiền có ký tự lạ hoặc dấu phẩy trộn vào có thể khiến tổng tiền dừng ở NaN cho đến tận màn hình. Sau khi chuyển đổi, bạn cần tự kiểm tra xem nó có thực sự thành công không.

Viết total === NaN không hoạt động như một cách kiểm tra ở đây. NaN thậm chí không bằng chính nó, nên kết quả luôn là false.

typeof NaN cũng trả về "number", nên kiểu cũng không phân biệt được nó. Dùng Number.isNaN (hàm trả về giá trị truyền vào có phải NaN hay không) để kiểm tra thay vào đó.

const inputAmount = "1,200";   // số tiền gõ vào có dấu phẩy

console.log(Number(inputAmount));         // NaN   dấu phẩy không chuyển đổi được
console.log(Number(inputAmount) + 500);   // NaN   phép tính sau đó cũng thành NaN

// Cả so sánh lẫn kiểu đều không phân biệt được nó
console.log(Number(inputAmount) === NaN);   // false
console.log(typeof NaN);                    // number

// Dùng hàm chuyên dụng để kiểm tra
console.log(Number.isNaN(Number(inputAmount)));   // true

Kiểm tra một giá trị gõ vào ô số tiền có thể coi là số hay không, và chỉ hiện thông báo khi không thể. inputAmount / validAmount đã được khai báo sẵn.

① Hiển thị kết quả chuyển inputAmount thành số.

② Kiểm tra và hiển thị kết quả ① có phải chuyển đổi thất bại hay không.

③ Chạy cùng kiểm tra đó trên validAmount và hiển thị kết quả.

④ Chỉ khi việc chuyển đổi inputAmount thất bại, hiển thị "Please enter the amount as a number."

JavaScript / TypeScript Editor

Chạy code để xem đầu ra

Đọc Một Chuỗi Bắt Đầu Bằng Số — parseInt và parseFloat

Đôi khi bạn nhận một chuỗi như "240px" hay "12.5%" — chữ số theo sau bởi một đơn vị. Number đọc toàn bộ chuỗi như một số, nên phần đơn vị còn dư biến kết quả thành NaN.

Khi bạn chỉ muốn các chữ số ở đầu, dùng một hàm khác.

parseInt (hàm lấy ra phần từ đầu đọc được thành số nguyên) lấy 240 từ "240px". 10 bạn truyền làm đối số thứ hai nghĩa là "đọc theo hệ chữ số thập phân thông thường, 0 đến 9". Bạn có thể bỏ qua nó, nhưng viết ra làm rõ ý định của bạn với bất kỳ ai đọc code.

Khi bạn muốn đọc cả đến dấu chấm thập phân, dùng parseFloat (hàm lấy ra phần từ đầu đọc được thành số thập phân). Cả hai đều trả về NaN nếu không có gì đọc được như số ngay từ đầu.

Number Khác parseInt / parseFloat Thế Nào
Number("240px")Cố đọctoàn bộNaNparseInt("240px")Từ đầu,như số nguyên240parseFloat("12.5%")Từ đầu,như số thập phân12.5
Number nhìn vào toàn bộ chuỗi. parseIntparseFloat chỉ lấy ra phần đọc được từ đầu, khác nhau ở việc có đọc đến dấu chấm thập phân hay không.
const cardWidth = "240px";   // width đọc từ một giá trị style

console.log(Number(cardWidth));         // NaN    không chuyển được toàn bộ
console.log(parseInt(cardWidth, 10));   // 240    chỉ phần đọc được từ đầu

console.log(parseFloat("12.5%"));       // 12.5   đọc đến dấu chấm thập phân
console.log(parseInt("12.5%", 10));     // 12     đọc như số nguyên bỏ phần thập phân

// Không đọc được gì nếu không bắt đầu bằng chữ số
console.log(parseInt("about 240px", 10));   // NaN

Chỉ Đọc Phần Đầu Có Thể Che Giấu Việc Cắt Ngầm

parseInt dừng ngay khi không thể đọc thêm được nữa. "1,200" ngăn cách bởi dấu phẩy thành 1, và "12.5" thành 12. Không cái nào báo lỗi, nên dùng Number cho các giá trị như số tiền cần đọc toàn bộ, và để dành parseInt cho chuỗi có gắn đơn vị.

Lấy đúng phần số ra khỏi một giá trị style và một chuỗi hiển thị. cardWidth / discountText đã được khai báo sẵn.

① Hiển thị kết quả chuyển cardWidth thành số dưới dạng toàn bộ chuỗi.

② Lấy phần số ra khỏi cardWidth dưới dạng số nguyên và hiển thị.

③ Lấy phần số ra khỏi discountText dưới dạng số thập phân và hiển thị.

④ Hiển thị giá trị của ② nhân đôi.

JavaScript / TypeScript Editor

Chạy code để xem đầu ra
QUIZ

Kiểm tra kiến thức

Hãy trả lời từng câu hỏi một.

Câu 1Chạy console.log("5" + 1); hiển thị gì?

Câu 2Kết quả của console.log(Number("240px")); là gì?

Câu 3Cái nào trong số này kiểm tra đúng một kết quả đã chuyển đổi có phải NaN không?