HomeLập trìnhJavaScriptCách làm việc...

Cách làm việc với chuỗi trong JavaScript – Mẹo để nối chuỗi hiệu quả


Mọi thứ bạn nhìn thấy trong trình duyệt – ngoại trừ hình ảnh và video – đều là một chuỗi.

Đó là lý do tại sao bạn nên học cách làm việc với họ đúng cách. Điều này sẽ tăng đáng kể hiệu suất của các ứng dụng web của bạn, cả trên giao diện người dùng và phụ trợ.

Cách nối chuỗi mặc định hoạt động – và các vấn đề của nó

Bạn nên biết gì về chuỗi trong lập trình? Các string là một kiểu dữ liệu nguyên thủy chứa một mảng các ký tự. Giá trị của các kiểu dữ liệu nguyên thủy là bất biến, vì vậy giá trị của chuỗi không thể thay đổi sau khi bạn đã khởi tạo nó. Điều này đúng với hầu hết các ngôn ngữ lập trình, kể cả JavaScript.

Nhưng chờ đã, khi bạn làm điều này:

let hello = "Hello";
hello += " world";
console.log(hello);

Bạn sẽ thấy Chào thế giới trên bảng điều khiển, có nghĩa là giá trị của hello biến đã thay đổi. Sao có thể như thế được? Làm cách nào JavaScript có thể thay đổi giá trị của một biến chuỗi và đồng thời giữ cho nó không thay đổi?

Điều này xảy ra vì JavaScript không trực tiếp thêm chuỗi thứ hai vào chuỗi đầu tiên. Thay vào đó, nó tạo ra một chuỗi trống thứ ba, sao chép các giá trị của cả hai chuỗi vào chuỗi đó và cuối cùng gán lại biến “hello” cho chuỗi thứ ba này.

Theo cách này, giá trị của chuỗi thứ ba chỉ được đặt một lần và giá trị của hai chuỗi ban đầu không thay đổi để đáp ứng quy tắc bất biến.

Đây là giao diện của toàn bộ quá trình nối chuỗi:

hình ảnh-99
1) Tạo một phạm vi trống trong bộ nhớ để giữ chuỗi mới. 2) Sao chép chuỗi đầu tiên vào đó. 3) Sao chép chuỗi thứ hai vào chuỗi 4) Gán lại biến “hello” cho chuỗi mới. 5) Phạm vi bộ nhớ trống được sử dụng để chứa hai chuỗi “Xin chào” và “thế giới” đầu tiên.

Bạn có thấy vấn đề gì ở đây không? Chúng ta có thể nói gì về hiệu suất của hoạt động này? Có vẻ như nó thực hiện nhiều thao tác hơn gấp năm lần so với bình thường và nó sử dụng bộ nhớ gấp hai lần ở bước 3 để chứa cùng một dữ liệu.

Một mặt, nó không phải là vấn đề lớn nếu chúng ta chỉ muốn nối hai chuỗi, bởi vì máy tính có thể thực hiện hàng triệu thao tác trong một giây. Nhưng vấn đề trở nên nghiêm trọng hơn nếu chúng ta cần xây dựng chuỗi dài.

Giả sử rằng chúng ta cần xây dựng một phần lớn nội dung HTML từ một mảng dữ liệu ngoài trong một vòng lặp. Trong trường hợp này, chuỗi HTML có thể trở nên rất lớn trong quá trình này và JavaScript sẽ tạo một bản sao của chuỗi này trên mỗi lần lặp của vòng lặp.

Đọc thêm  Hướng dẫn API tìm nạp JavaScript với các ví dụ về tiêu đề và bài viết tìm nạp JS

Ví dụ, hãy xem đoạn mã tạo một chuỗi lớn trong một vòng lặp, bằng cách nối chuỗi ban đầu một trăm triệu lần.

let str = "Hello";

console.log("START",new Date().toUTCString());

for (let index=0;index<100000000;index++) {
    str += "!";
}

console.log("END",new Date().toUTCString());
console.log(str.length);

Mã này nối thêm dấu “!” biểu tượng cho chuỗi một trăm triệu lần. Trong một ví dụ thực tế, bạn có thể cho rằng thay vì dấu ‘!’ biểu tượng đó sẽ là dữ liệu thực từ một nguồn bên ngoài sẽ được hiển thị sau này.

Ngoài ra, mã này xuất ra ngày và giờ hiện tại trước và sau vòng lặp giúp đo thời gian cần thiết. Cuối cùng, nó hiển thị độ dài của chuỗi đã xây dựng.

Khi tôi chạy cái này trong trình duyệt Google Chrome của mình, phải mất một lúc để hoàn thành. Cuối cùng, nó hiển thị như sau trên bảng điều khiển JavaScript:

hình ảnh-100
Bảng điều khiển Javascript cho thấy mã nối chuỗi cơ bản đã tạo chuỗi 100000005 ký tự trong 1 phút 26 giây.

Như bạn có thể thấy, phải mất 1 phút 26 giây và xuất ra độ dài chính xác của chuỗi được nối. Nhưng khi tôi chạy mã này trên một máy tính khác, mã này đã làm hỏng trình duyệt và tôi thấy đầu ra sau:

hình ảnh-101
Trình duyệt web gặp sự cố

Nếu bạn còn nhớ thuật toán nối chuỗi cơ bản được mô tả ở trên, bạn sẽ hiểu rõ tại sao điều này có thể xảy ra.

Thuật toán nối chuỗi mặc định quá kém hiệu quả và lãng phí rất nhiều bộ nhớ. Trong ví dụ này, nó sao chép từ 1 đến một trăm triệu ký tự một trăm triệu lần trong khi lặp qua vòng lặp. Dung lượng bộ nhớ được sử dụng cho việc này thậm chí còn khó nhận ra.

Điều này có nghĩa là chương trình có gặp sự cố hay không phụ thuộc vào dung lượng bộ nhớ trống khả dụng và cách hoạt động của trình thu gom rác bộ nhớ trong triển khai công cụ JavaScript cụ thể để xóa các chuỗi tạm thời không sử dụng.

Cách cải thiện thuật toán nối chuỗi mặc định

Thuật toán nối chuỗi JavaScript mà chúng tôi đã thảo luận ở trên không khẳng định là chính xác về mặt học thuật. Các triển khai khác nhau của công cụ JavaScript có thể sử dụng các cơ chế xử lý bộ nhớ và tối ưu hóa chuỗi khác nhau.

Nhưng bạn không nên tin rằng mã của bạn sẽ luôn chạy trong các công cụ như vậy.

Ví dụ: trong phiên bản Google Chrome mới nhất tại thời điểm viết bài này, nối chuỗi hoạt động như minh họa trong ảnh chụp màn hình ở trên. Vì vậy, mục đích của bài viết này là chỉ ra cách làm việc với các chuỗi hiệu quả hơn, bất kể nó được triển khai theo mặc định như thế nào.

Đọc thêm  Cách sử dụng Async/Await trong JavaScript với mã JS mẫu

Chắc chắn chúng ta nên tìm cách thực hiện chính xác những gì chúng ta cần bằng cách nối hai chuỗi bằng một thao tác đơn lẻ. Nhiều ngôn ngữ lập trình khác, như Java hoặc Go (cũng sử dụng các chuỗi bất biến) có một công cụ gọi là StringBuilder. Đây là một đối tượng trợ giúp cho phép bạn xây dựng một chuỗi từ các phần tử của mảng hoặc từ các đối tượng có thể thay đổi khác.

JavaScript không có sẵn tính năng này, nhưng không khó để tự thực hiện một ý tưởng tương tự.

Bạn có thể viết cùng một chuỗi theo một cách khác:

let hello = ["Hello"];

Đây không phải là một chuỗi – đúng hơn, đây là một mảng có một chuỗi. Thay vì các chuỗi, các mảng có thể thay đổi được và bạn chỉ có thể thay đổi chúng bằng cách thêm các mục. Điều này có nghĩa là nếu bạn chạy cái này:

hello.push(" world");

Javascript sẽ chỉ thay đổi mảng bằng cách nối thêm mục “thế giới” vào cuối. Điều này sẽ được thực hiện trong một thao tác duy nhất, sau đó mảng sẽ chứa các nội dung sau:

["Hello"," world"]

Bằng cách này, bạn có thể nối bao nhiêu chuỗi tùy ý vào mảng này với chi phí rất thấp.

Cuối cùng, để tạo chuỗi từ nó, bạn có thể chạy lệnh join thao tác trên mảng:

hello = hello.join("");
console.log(hello);

Sau đó, đầu ra của biến “hello” sẽ chứa chuỗi “Hello world”. Trên thực tế, thao tác nối cũng tạo ra một chuỗi rỗng và sau đó sao chép các phần tử từ mảng sang nó. Nhưng điều này chỉ xảy ra một lần (thay vì mọi lần khi nối chuỗi).

Cách tiếp cận này làm tăng đáng kể tốc độ nối chuỗi trong một vòng lặp. Hãy thay đổi ví dụ về vòng lặp để sử dụng mảng thay vì chuỗi:

let str = ["Hello"];

console.log("START",new Date().toUTCString());

for (let index=0;index<100000000;index++) {
    str.push("!");
}

str = str.join("");

console.log("END",new Date().toUTCString());
console.log(str.length);

Sau khi chạy cái này trên cùng một trình duyệt, tôi nhận được kết quả như sau:

hình ảnh-102
Bảng điều khiển Javascript cho thấy mã nối chuỗi bằng cách sử dụng mảng đã tạo chuỗi 100000005 ký tự trong 8 giây.

Như bạn có thể thấy, kết quả tương tự đạt được trong 8 giây, nhanh hơn gấp 10 lần so với nối chuỗi thông thường.

Suy nghĩ cuối cùng

Như bạn có thể thấy, chỉ bằng cách thay đổi ba dòng mã, bạn có thể tăng đáng kể hiệu suất của quy trình xử lý dữ liệu.

Là một phần trong quá trình thực hành của tôi, tôi có một khách hàng có trang web bị chậm do xử lý chuỗi không hiệu quả mà anh ấy đã cố gắng giải quyết bằng cách lưu nội dung vào bộ nhớ đệm trong CloudFlare. Anh cũng cân nhắc nghiêm túc việc chuyển sang AWS để tăng thông lượng dữ liệu nhằm giải quyết những vấn đề này. Nhưng nó là đủ để thực hiện đánh giá mã để sửa nó.

Đọc thêm  Thuộc tính trong javascript là gì? - JavaScript

Bạn có thể sử dụng phương pháp được mô tả trong hướng dẫn này khi tạo chuỗi trong vòng lặp từ luồng dữ liệu ngoài có kích thước thay đổi trong JavaScript. Chỉ cần thêm từng chuỗi vào một mảng và cuối cùng nối chúng thành một chuỗi trước khi xuất.

Các ngôn ngữ lập trình khác khuyến nghị sử dụng các đối tượng StringBuilder hoặc StringBuffer bên trong để nối chuỗi.

Đối với Javascript, chúng tôi đã xây dựng StringBuilder đơn giản chỉ có thể nối thêm các chuỗi. Để thực hành, bạn có thể mở rộng nó và thêm các phương thức khác nhau để “chắp thêm”, “chèn” hoặc “xóa” các chuỗi khỏi một mảng. Bạn cũng có thể tạo một lớp đóng gói một biến mảng với các hàm để thao tác các chuỗi con trong mảng này và xây dựng chuỗi cuối cùng từ nó khi được yêu cầu.

Một số hạn chế đối với phương pháp này

Khi thêm các phần tử vào một mảng, điều quan trọng là phải ghi nhớ các giới hạn hiện có về số phần tử mảng. Nếu bạn không tính đến chúng, bạn có thể gặp phải lỗi “RangeError: phạm vi mảng không hợp lệ”. Bạn có thể tìm hiểu thêm về các giới hạn tại đây.

Nếu số dòng được thêm vào trong vòng lặp vượt quá các giới hạn này, thì bạn sẽ phải định kỳ xóa mảng vào các bộ đệm chuỗi tạm thời, sau đó hợp nhất các bộ đệm này.

Để giúp bạn làm việc với các chuỗi hiệu quả hơn nữa, có sẵn nhiều thuật toán xử lý chuỗi tuyệt vời hơn.

Một trong số chúng nhanh nhất dựa trên cấu trúc dữ liệu có tên là “Rope”. Nó được phát minh để xử lý các hoạt động trên các chuỗi lớn một cách nhanh chóng. Bạn có thể đọc thêm về nó ở đây. Phương pháp này phức tạp hơn phương pháp đã thảo luận ở trên, nhưng bạn có thể bắt đầu sử dụng lại một trong các triển khai Javascript của Rope trong các dự án của bạn (bạn có thể tìm thấy chúng tại đây và tại đây).

Cảm ơn bạn đã đọc!

Chúc mọi người may mắn và mã hóa vui vẻ 🙂

Vui lòng kết nối và theo dõi tôi trên các mạng xã hội bên dưới nơi tôi đăng thông báo về các bài viết sắp tới của mình, tương tự như bài viết này và các tin tức phát triển phần mềm khác:

LinkedIn: https://www.linkedin.com/in/andrey-germanov-dev/
Facebook: https://web.facebook.com/AndreyGermanovDev
Twitter: https://twitter.com/GermanovDev





Zik.vn – Biên dịch & Biên soạn Lại

spot_img

Create a website from scratch

Just drag and drop elements in a page to get started with Newspaper Theme.

Buy Now ⟶

Bài viết liên quang

DMCA.com Protection Status