Ba cách viết một chuỗi
JavaScript cho bạn ba loại dấu bao chuỗi. Hai loại dùng thay cho nhau được; loại thứ ba làm được nhiều hơn.
Dấu nháy đơn và nháy kép tạo ra chuỗi giống hệt nhau: chọn một loại và dùng nhất quán (phần lớn codebase hiện đại mặc định dùng nháy đơn hoặc để công cụ định dạng quyết định). Backtick tạo ra template literal, và đây là chỗ thú vị: nó cho phép nội suy và trải trên nhiều dòng.
Bên dưới, cả ba đều tạo ra cùng một kiểu chuỗi (string) nguyên thủy. Dấu bao chỉ là lựa chọn về cú pháp.
Chuỗi thoát và dấu nháy bên trong chuỗi
Bên trong chuỗi, dấu gạch chéo ngược mở đầu một chuỗi thoát (escape sequence). Các chuỗi thoát hay gặp là \n (xuống dòng), \t (tab), \\ (dấu gạch chéo ngược theo nghĩa đen), và \" / \' để chèn đúng loại dấu nháy bạn đã dùng để mở chuỗi:
Bạn tránh được phần lớn ký tự thoát bằng cách chọn loại dấu nháy còn lại: 'She said "hi" and left.' không cần dấu gạch chéo ngược nào. Thực dụng hơn giáo điều.
Template literal: siêu năng lực của backtick
Template literal làm được hai việc mà chuỗi thường không làm được. Thứ nhất, nó nội suy biểu thức bằng ${...}:
Mọi thứ bên trong ${...} là một biểu thức JavaScript, không chỉ là tên biến. Phép tính, lời gọi hàm, toán tử ba ngôi, thậm chí template literal khác: tất cả đều được:
Thứ hai, template literal trải trên nhiều dòng mà không cần \n:
Xuống dòng trong mã nguồn trở thành xuống dòng trong chuỗi. Với bất cứ thứ gì dài hơn một nhãn ngắn, backtick gần như luôn là lựa chọn đúng.
Nội suy tốt hơn nối chuỗi
Trước khi có template literal, cách duy nhất để tạo chuỗi động là toán tử +:
Cả hai cho ra kết quả giống hệt nhau. Template literal đọc lên giống câu nó đang tạo, còn phiên bản + bắt bạn theo dõi dấu nháy, dấu cách và toán tử nối. Chỉ dùng + khi bạn nối thêm một thứ nhỏ vào chuỗi có sẵn. Với mọi thứ dài hơn, hãy dùng backtick.
Chuỗi là bất biến
Mọi phương thức trông như đang sửa chuỗi thực ra đều trả về chuỗi mới:
Muốn giữ thay đổi, bạn phải gán kết quả cho đâu đó. Người mới hay vấp ở đây khi viết s.replace("a", "b") và mong s thay đổi. Nó không đổi. Chuỗi trong JavaScript giống số: bản thân giá trị không thay đổi được, chỉ có biến là trỏ sang giá trị mới được.
Những phương thức chuỗi bạn thực sự dùng
API chuỗi đầy đủ rất lớn. Đây là vài phương thức bạn sẽ dùng hằng ngày:
Vài điều nên ghi nhớ:
lengthlà thuộc tính, không phải phương thức: không có dấu ngoặc.replacevới một chuỗi thông thường chỉ thay chỗ khớp đầu tiên. DùngreplaceAllhoặc regex có cờgđể thay mọi chỗ khớp.slice(start, end)nhận một khoảng nửa mở: ký tự ở vị tríendkhông được lấy.- Chỉ số âm trong
sliceđếm từ cuối:s.slice(-3)cho ba ký tự cuối.
Truy cập theo chỉ số và duyệt ký tự
Chuỗi hoạt động giống mảng ký tự chỉ đọc. Bạn truy cập theo chỉ số và duyệt được, nhưng không gán được vào một vị trí:
for...of duyệt theo code point, xử lý đúng phần lớn Unicode. Dùng chỉ số (s[i]) thì duyệt theo code unit UTF-16, có thể cắt đôi emoji và các ký tự ngoài BMP. Với văn bản thông thường, cả hai đều ổn; với bất cứ thứ gì do người dùng nhập, hãy ưu tiên for...of hoặc [...s].
Tagged template literal
Còn một dạng nữa đáng biết, dù bạn không viết thường xuyên. Một template literal có tag gọi một hàm với các mảnh chuỗi tĩnh và các giá trị nội suy làm đối số riêng:
Hàm tag quyết định chuỗi cuối cùng trông như thế nào: nó có thể thoát HTML, tạo SQL an toàn, biên dịch CSS hoặc phân tích GraphQL. Bạn sẽ gặp mẫu này trong thư viện (styled-components, graphql-tag, lit-html) nhiều hơn hẳn số lần tự viết. Nhưng khi cần xử lý chuỗi theo cách riêng, tag là công cụ gọn gàng nhất.
Những lỗi thường gặp
Danh sách ngắn những điều hay làm người học vấp:
- Quên gán kết quả của phương thức. Riêng
s.trim()không làm gì cả. - Dùng
+để nối số với chuỗi."Age: " + 30 + 5là"Age: 305", không phải"Age: 35". Template literal tránh được chuyện này:`Age: ${30 + 5}`. - Nhầm
==khi so sánh chuỗi. Phần so sánh bằng sẽ nói kỹ sau, nhưng"1" == 1làtruecòn"1" === 1làfalse. Hãy ưu tiên===. - Cho rằng
lengthbằng số ký tự. Với emoji và các ký tự dùng cặp surrogate,"🙂".lengthlà 2, không phải 1. Dùng[..."🙂"].lengthhoặcArray.from(s).lengthkhi bạn cần số ký tự mà người dùng nhìn thấy.
Tiếp theo: number và BigInt
Chuỗi là một kiểu nguyên thủy; số là một kiểu khác, và hệ thống số của JavaScript có những điểm kỳ quặc riêng: một kiểu Number duy nhất cho cả số nguyên lẫn số thực, cộng thêm BigInt cho khi như thế là chưa đủ. Đó là trang tiếp theo.
Câu hỏi thường gặp
Dấu nháy và backtick trong JavaScript khác nhau thế nào?
Dấu nháy đơn (') và nháy kép (") đều tạo chuỗi thông thường, dùng thay cho nhau được. Backtick (`) tạo template literal, hỗ trợ nội suy bằng ${...} và trải trên nhiều dòng mà không cần ký tự thoát. Hãy dùng backtick mỗi khi bạn chèn biến hoặc viết văn bản nhiều dòng.
Làm sao chèn biến vào chuỗi trong JavaScript?
Dùng template literal: bọc chuỗi trong backtick và đặt biểu thức bên trong ${...}. Ví dụ, `Hello, ${name}!` tính giá trị name rồi ghép kết quả vào. Bạn đặt được bất kỳ biểu thức nào bên trong: ${a + b}, ${user.name.toUpperCase()}, thậm chí template literal lồng nhau.
Chuỗi trong JavaScript có bất biến không?
Có. Mọi phương thức chuỗi (toUpperCase, slice, replace và các phương thức khác) đều trả về chuỗi mới thay vì sửa chuỗi gốc. Nếu bạn viết s.toUpperCase() mà không gán kết quả cho đâu cả, sẽ chẳng có gì thay đổi. Mô hình này giống chuỗi trong Python hay Java.
Tagged template literal là gì?
Tagged template literal gọi một hàm với các mảnh chuỗi của template và các giá trị nội suy làm đối số riêng: tag`Hello, ${name}` gọi tag(["Hello, ", ""], name). Các thư viện như styled-components và graphql-tag dùng tag để phân tích hoặc biến đổi template trước khi tạo ra giá trị cuối cùng.