NỘI DUNG BÀI VIẾT
JavaScript là một trong những ngôn ngữ lập trình đứng đầu trong bảng xếp hạng các ngôn ngữ lập trình phổ biến nhất hiện nay. Bởi tính đa dụng của nó, JavaScript vừa có thể phát triển front-end, back-end, mobile, thậm chí cả nhúng, dữ liệu,…
Bài viết này nhằm mục đích tìm hiểu sâu hơn về JavaScript và cách JavaScript hoạt động phía sau “cánh gà”.
Tổng quan
Hầu như mọi người đều đã nghe về V8 Engine như một khái niệm và đều biết rằng JavaScript là đơn luồng (single-thread) hoặc nó sử dụng hàng đợi callback (callback queue).
Trong bài viết này, chúng ta sẽ đi qua tất cả các khái niệm này một cách chi tiết và giải thích cách JavaScript hoạt động. Bằng cách biết những chi tiết này, bạn sẽ có thể viết các ứng dụng hiệu năng cao và tận dụng đúng cách các API được cung cấp.
Nếu bạn chưa quen với JavaScript, bài viết sẽ là cơ hội giúp bạn hiểu lý do tại sao JavaScript quá “kỳ lạ” so với các ngôn ngữ lập trình khác. Và nếu bạn là một lập trình viên JavaScript có kinh nghiệm, hy vọng, sẽ cung cấp cho bạn một số thông tin chi tiết về cách JavaScript hoạt động mà bạn vẫn sử dụng nó hàng ngày.
Chúng ta sẽ thảo luận về hoạt động bên trong của JavaScript trong môi trường thời gian chạy và trình duyệt qua các phần như sau:
- JavaScript Engine
- JavaScript Runtime Environment
- Call Stack
- Concurrency và Event Loop
Cách JavaScript hoạt động
JavaScript Engine
JavaScript là một ngôn ngữ lập trình thông dịch. Có nghĩa là mã nguồn không được biên dịch thành mã nhị phân trước khi thực thi.
Làm cách nào máy tính của bạn có thể hiểu được những việc cần làm với một tập lệnh văn bản thuần túy?
Đó là công việc của JavaScript Engine. JavaScript Engine chỉ đơn giản là một chương trình máy tính thực thi mã JavaScript.
Các JavaScript Engine được tạo sẵn trong tất cả các trình duyệt hiện đại ngày nay. Khi tệp JavaScript được tải trong trình duyệt, JavaScript Engine sẽ thực thi từng dòng của tệp từ trên xuống dưới. JavaScript Engine sẽ phân tích cú pháp từng dòng mã, chuyển nó thành mã máy và sau đó thực thi nó.
Mỗi trình duyệt đều có JavaScript Engine riêng nhưng JavaScript Engine được biết đến nhiều nhất là V8 của Google. V8 cung cấp sức mạnh cho Google Chrome và Node.js – hay còn gọi là JavaScript Runtime.
Engine | Trình duyệt |
V8 | Google Chrome, Node.js |
Spider Monkey | Mozilla Firefox |
JavaScriptCore | Safari |
Chakra | Microsoft Edge |
Engine bao gồm 2 thành phần chính:
- Memory Heap: là nơi xảy ra việc cấp phát bộ nhớ
- Call Stack: là nơi chứa các khung ngăn xếp khi mã được thực thi
Bất kỳ công cụ JavaScript nào luôn chứa 1 Call Stack và 1 Memory Heap. Call Stack là nơi mã của chúng ta thực sự được thực thi. Sau đó, Heap là một vùng bộ nhớ không có cấu trúc lưu trữ tất cả các đối tượng mà ứng dụng của chúng ta cần.
Xem thêm: Node.js là gì?
JavaScript Runtime Environment
JavaScript Engine không chạy riêng lẻ. Nó chạy bên trong một môi trường được gọi là JavaScript Runtime Environment cùng với nhiều thành phần khác. JRE chịu trách nhiệm làm cho JavaScript không đồng bộ. Đó là lý do JavaScript có thể thêm trình xử lý sự kiện và thực hiện các yêu cầu HTTP không đồng bộ.
JRE giống như một thùng chứa bao gồm các thành phần sau:
- JS Engine
- Web API
- Callback Queue và Message Queue
- Event Table
- Event Loop
Và sau đó, chúng ta có Event Loop và Callback Queue.
Call Stack
JavaScript là một ngôn ngữ lập trình đơn luồng, có nghĩa là nó có một Call Stack duy nhất. Do đó, nó chỉ có thể làm một việc tại một thời điểm.
Call Stack là một cấu trúc dữ liệu ghi lại về vị trí của chúng ta trong chương trình. Nếu chúng ta bước vào một hàm, chúng ta sẽ đặt nó ở trên cùng của ngăn xếp. Nếu chúng ta trả về từ một hàm, chúng ta sẽ bật ra khỏi đầu ngăn xếp. Đó là tất cả những gì ngăn xếp có thể làm.
function multiply(x, y) {
return x * y;
}
function printSquare(x) {
var s = multiply(x, x);
console.log(s);
}
printSquare(5);
Code language: JavaScript (javascript)
Khi Engine bắt đầu thực thi mã này, Call Stack sẽ trống. Sau đó, các bước sẽ như sau:
Mỗi mục trong Call Stack được gọi là Stack Frame.
Chạy mã trên một luồng có thể khá dễ dàng vì bạn không phải đối phó với các tình huống phức tạp phát sinh trong môi trường đa luồng. Ví dụ như deadlock chẳng hạn…
Nhưng chạy trên một luồng cũng khá hạn chế. Vì JavaScript có một Call Stack duy nhất.
Concurrency và Event Loop
Điều gì xảy ra khi bạn có các lệnh gọi hàm mà cần một lượng lớn thời gian để được xử lý trong Call Stack? Ví dụ: hãy tưởng tượng rằng bạn muốn thực hiện một số chuyển đổi hình ảnh phức tạp với JavaScript trong trình duyệt.
Bạn có thể tự hỏi – tại sao đây lại là vấn đề? Vấn đề là trong khi Call Stack có các hàm cần thực thi, trình duyệt thực sự không thể làm bất cứ điều gì khác – nó đang bị chặn. Trình duyệt không thể hiển thị, không thể chạy bất kỳ mã nào khác, nó chỉ bị kẹt. Và điều này tạo ra vấn đề nếu bạn muốn có giao diện người dùng mượt mà đẹp mắt trong ứng dụng của mình.
Và đó không phải là vấn đề duy nhất. Khi trình duyệt của bạn bắt đầu xử lý quá nhiều tác vụ trong Call Stack, trình duyệt có thể ngừng phản hồi trong một thời gian khá dài. Và hầu hết các trình duyệt thực hiện hành động bằng cách đưa ra lỗi, hỏi bạn xem bạn có muốn chấm dứt trang web hay không.
Kết luận
Trên đây là những kiến thức cơ bản nhất về cách JavaScript hoạt động. Tất nhiên là nó vẫn chưa đủ. Ta phải đi sâu nữa vào từng phần để hiểu rõ hơn về cách JavaScript hoạt động nếu bạn muốn trở thành 1 Software Architect về các “công nghệ lõi”. Nhưng nếu chỉ dừng lại ở mức độ hiểu biết, kiến thức trong bài viết là quá đủ vì rất ít khi bạn phải sử dụng chúng.
Cảm ơn bạn đã theo dõi bài viết!
Các bạn có thể tham khảo các bài viết hay về JavaScript tại đây.
Hãy tham gia nhóm Học lập trình để thảo luận thêm về các vấn đề cùng quan tâm.
TỔNG HỢP TÀI LIỆU HỌC LẬP TRÌNH CƠ BẢN CHO NGƯỜI MỚI BẮT ĐẦU
KHOÁ HỌC BOOTCAMP JAVA/JAVASCRIPT/PHP TRỞ THÀNH LẬP TRÌNH VIÊN TRONG 5-6 THÁNG