Hiểu hơn về cách hoạt động của JavaScript Engine

JavaScript thì “ngầu” thật đấy, nhưng làm sao để máy có thể hiểu được code bạn viết nghĩa là gì? Nếu là lập trình viên JavaScript, bạn sẽ không phải đích thân đi xử lý các compiler. Tuy nhiên, bạn vẫn cần phải biết những kiến thức căn bản về cơ chế vận hành của JavaScript, cũng như cách nó xử lý phần JS code bạn đã viết và “gửi gắm” nó sang ngôn ngữ mà máy móc có thể hiểu được.  

Note: Bài viết này sẽ nói theo V8 engine và các trình duyệt dựa trên Chrome. 

HTML parser sẽ gặp tag script cùng 1 source. Code từ source này sẽ được load hoặc từ network, cache, hoặc một service worker đã cài trước. Response trả về sẽ là một đoạn script dưới dạng 1 stream byte, phần này sẽ do byte stream decoder xử lý. Byte stream decoder sẽ decode chuỗi byte này ra khi nó được down về.

Hiểu hơn về cách hoạt động của JavaScript Engine

Byte stream decoder sẽ khởi tạo các mã token từ luồng byte đã được decode. Ví dụ, 0066 decode thành f, 0075 thành u, 006e thành n, 0063 thành c, 0074 thành t, 0069 thành i, 006f thành o, 006e thành n với khoảng trắng. Đây là keyword dành riêng cho JavaScript, mã token được tạo và gửi đến parser (và cả các pre-parser nữa, tôi không có gif để mô tả kỹ hơn nhưng tôi sẽ giải thích thêm bên dưới). Và đối với phần còn lại của byte stream cũng sẽ diễn ra tương tự.

Hiểu hơn về cách hoạt động của JavaScript Engine

Engine sử dụng 2 parser: pre-parser và parser. Pre-parser có nhiệm vụ kiểm tra lỗi cú pháp từ các mã token. Từ đó làm giảm thiểu được không ít thời gian trong việc tìm lỗi trong code, dù đương nhiên sẽ có trường hợp sau đó parser vẫn phát hiện thêm lỗi. 

Trường hợp nếu không có lỗi, parser sẽ tạo ra các node được dựa trên mã token nó nhận được từ byte stream decoder. Với các node này, nó tạo ra Abstract Syntax Tree (AST).

Hiểu hơn về cách hoạt động của JavaScript Engine

Tiếp theo là đến công đoạn của interpreter (trình phiên dịch). Interpreter đi qua AST và tạo byte code dựa trên thông tin AST chứa. Sau khi hoàn thành công đoạn tạo byte code, AST sẽ bị xóa để giải phóng bộ nhớ. Cuối cùng chúng ta đã có “nguyên liệu” có thể làm việc máy.

Hiểu hơn về cách hoạt động của JavaScript Engine

Mặc dù byte code đã khá nhanh nhưng vẫn có thể tối ưu hóa tốc độ hơn nữa. Thông tin được tạo ra khi byte code hoạt động. Nó có thể phát hiện những hành vi nào thường diễn ra, và loại data được sử dụng. Có thể bạn thường lặp đi lặp lại một chức năng, thì đây cũng là lúc tối ưu hóa để đẩy nhanh tốc độ.

Byte code cùng với feedback type đã được tạo sẽ được gửi đến optimizing compiler (compiler tối ưu hoá). Optimizing compiler lấy byte code và type feedback để tạo nên machine code cực kỳ tối ưu hóa.

Hiểu hơn về cách hoạt động của JavaScript Engine

JavaScript là ngôn ngữ dạng dynamic typing, vì các loại data luôn thay đổi. Nếu JavaScript engine phải kiểm tra liên tục giá trị của mỗi loại data thì sẽ rất chậm. 

Thay vào đó engine có thể sử dụng bộ nhớ đệm nội tuyến (inline caching), có chức năng lưu trữ code trong bộ nhớ và hy vọng trả về cùng một giá trị với cùng một behavior trong tương lai. Giả sử một hàm nhất định được sử dụng 100 lần và luôn trả về cùng một giá trị, thì lần thứ 101 sử dụng nó cũng sẽ trả đúng về giá trị này.

Ví dụ như có hàm chức năng sum như sau, luôn được gọi với giá trị numerical (số) dưới dạng đối số (argument) mỗi lần:

Hiểu hơn về cách hoạt động của JavaScript Engine

Kết quả sẽ trả về số 3! Lần tới, khi chúng ta nhập lại hàm này, nó sẽ giả định chúng ta lại nhập hai giá trị numerical.

Nếu đúng, thì nó sẽ yêu cầu thêm dynamic lookup, và chỉ có thể sử dụng kết quả được lưu trong bộ nhớ đã lưu trước đó. Nếu không phải, nó sẽ de-optimize (đảo ngược tối ưu hóa) code và trả lại về byte code ban đầu thay vì machine code đã được tối ưu hóa.

Tham khảo tuyển dụng javascript lương cao trên TopDev

Ví dụ lần sau khi đã nhập, tôi sẽ truyền một string (chuỗi) thay vì một number (số). Vì JavaScript là dynamical type nên sẽ không xảy ra bất kỳ lỗi nào.

Hiểu hơn về cách hoạt động của JavaScript Engine

Điều này nghĩa là số 2 sẽ bị ép thành một chuỗi và thay vào đó hàm sẽ trả về chuỗi “12”. Nó sẽ quay lại xử lý byte code đã nhập và cập nhật thêm type feedback.

Tôi mong bài viết trên phần nào giúp ích được các bạn dù có nhiều phần tôi chưa đề cập (JS heap, call stack,..) Thế nhưng tôi nghĩ các bạn hoàn toàn có thể tự nghiên cứu nếu có hứng thú với JavaScript, với V8 là mã nguồn mở và hiện có khá nhiều tài liệu về cách hoạt động khá thú vị!

 

TopDev via dev.to

Tuyển dụng nhân viên IT đãi ngộ tốt trên TopDev