Menu

Chuỗi và template literal trong JavaScript: backtick, nội suy, phương thức

Chuỗi hoạt động thế nào trong JavaScript: dấu nháy, backtick, nội suy, văn bản nhiều dòng và những phương thức bạn thực sự hay dùng.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

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ớ:

  • length là thuộc tính, không phải phương thức: không có dấu ngoặc.
  • replace với một chuỗi thông thường chỉ thay chỗ khớp đầu tiên. Dùng replaceAll hoặ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í end khô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 + 5 là "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" == 1 là true còn "1" === 1 là false. Hãy ưu tiên ===.
  • Cho rằng length bằng số ký tự. Với emoji và các ký tự dùng cặp surrogate, "🙂".length là 2, không phải 1. Dùng [..."🙂"].length hoặc Array.from(s).length khi 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.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu