Understanding Javascript: Execution Context in Javascript

Nếu bạn muốn trở thành một DEV JS chân chính thì bạn cần phải biết cách các chương trình JS được thực thi bên trong. Sự hiểu biết của bạn về cách các tệp JS được thực thi đằng sau hậu trường của các Runtime (Node.js và Chrome) rất quan trọng nó chính là nền tảng để các bạn hiểu được các khái niệm nâng cao trong JS như Hosting, Scope hay Closure.


Execution Context là gì ?

Nói một cách đơn giản thì Ec là một khái niệm trừu tượng về một môi trường nơi mà code JS trong hàm của bạn có thể được tính toán và thực thi. Hiểu đơn giản thì EC là nơi mà code của bạn được thực thi, nó như một trình bao bọc quản lý khối code (trong hàm) của bạn mỗi khi chạy.Bất cứ khi nào một hàm được chạy nó sẽ được chạy trong một EC của riêng nó.

Có những loại Execution Context nào ?

- Execution Context Global : Đây là EC mặc định, mỗi khi bạn thực thi tệp JS của mình thì ban đầu ECG sẽ tự động được tạo ra và chạy.Trong quá trình tạo ECG thì 2 thứ được tạo ra :
+ Global Object === Window (trong Chrome)
+ This (Object) 
=> Bạn chú ý rằng chỉ có duy nhất một ECG được tạo ra.
- Execution Context Function : Như phần giới thiệu đã nói mỗi khi một hàm được invoked , một EC được tạo ra và chạy , mỗi hàm của một EC riêng .
- Execution Context Eval : Chúng ta sẽ không đề cập đến vấn đề này.


Call Stack là gì ?

Call stack còn được gọi là một stack có cấu trúc dữ liệu LIFO (vào trước ra trước ) nó được sử dụng để lưu trữ tất cả các EC trong quá trình tệp JS được thực thi, nó cho chúng ta biết hàm nào đang chạy, tại một thời điểm nhất định chỉ có 1 hàm được chạy trên 1 Call stack khi một hàm được call thì sẽ có một EC được tạo ra trong quá trình chạy thì EC sẽ được đẩy lên đầu của ECS để chạy.Khi EC chạy xong (hàm được chạy xong) thì nó sẽ được đẩy ra khỏi call stack.



Execution Context  được tạo ra như thế nào ?

Khi đọc đến đây chắc một số bạn cũng đoán được EC được tạo qua 2 giai đoạn
- Create Phase
- Execution Phase

Create Phase
EC được tạo ra trong giai đoạn Create Phase.Những điều sau đây xảy ra trong create phase. 

1.Thành phần Lexical Environment được tạo ra.
2.Thành phần Variable Environment được tạo ra.


Có những bài viết đề cập trong giai đoạn Create Phase thì Javascript Engine sẽ đi qua 3 hoặc 4 bước sau :
B1: Tạo ra một đối tượng toàn cầu (Window hoặc Global) (chỉ ở global execution context)
B2: Tạo ra từ khóa this
B3: Thiết lập không gian bộ nhớ cho các biến và hàm
B4: Gán giá trị cho các biến = undefine và thiết lập giá trị cho các hàm dạng statement.
=> Đây là quan điểm không sai nhưng vẫn chưa đầy đủ, để cho dễ hình dung và tránh gây nhầm lẫn chúng ta sẽ chia ra là 2 thành phần là Lexical Environemt và Variable Environment.

Như vậy biểu diễn một cách đơn giản EC sẽ nhìn giống hình sau :


Lexical Environment là gì ?

Một Lexical Environmnet là một cấu trúc chứa Định danh (Indentifier - là ánh xạ của biến ) , ở đây định danh đề cập đến tên của biến/hàm và biến là tham chiếu đến một đối tượng thực tế (đối tượng có thể là bất kì vùng nhớ nắm giữ giá trị kiểu nào , string boolean , object)

Ví dụ bạn hãy xem đoạn code này :



Thì Lexical Environtment sẽ trông như thế này :
Mỗi Lexical Environment sẽ có 3 thành phần :

1 : Evironment Record
2 : Reference to the outer environment
3 : This binding


1. Environment Record 

=> Là nơi lưu trữ các biến và hàm (kiểu declarations) bên trong Lexical Environment.
Phức tạp hơn rằng sẽ có 2 kiểu Environment Record :
+ Declarations Environment Record :
Như tên của nó gợi ý => nơi lưu trữ biến và hàm (declarations) . Tổng kết thì nó là Lexical Environment cho hàm mà chứa mọi bản ghi của hàm và biến (delarations)
+
Object environemnt record : Lexical Environment cho toàn bộ global code chứa một bản ghi môi trường khách quan.Ngoài khả năng lưu trữ các biến và hàm (declarations) , Object Environment Record còn lưu trữ một object liên kết (binding) đến global object (window). Vì vậy đối với mỗi thuộc tính rằng buộc trong đối tượng (ví dụ như trong browser nó chứa các thuộc tính và phương thức do trình duyệt cung cấp) thì một entry mới được tạo ra trong bản ghi.

Chú ý : Trong hàm, Environment Record cũng chứa một đối tượng là đối số (argument) , đối tượng này chứa ánh xạ giữa các chỉ mục và đối số được truyền vào hàm và số lượng đối số được truyền cho hàm.Ví dụ.



2.Reference to the Outer Environment

Tham chiếu đến môi trường bên ngoài có nghĩa là nó có quyền truy cập vào Lexical Environment bên ngoài của nó.Hiểu đơn giản rằng JSE không tìm thấy biến trong LE của hàm đang chạy (Lexical Environment của Execution Context đang chạy) thì nó có thể tìm kiếm biến trong LE bên ngoài của nó.

3.This Binding

Trong thành phần này , giá trị của object 'this' được xác định. Trong ECG thì giá trị của this trỏ đến Global Object (Window trong browser)
Trong Execution Context Function giá trị của 'this' phụ thuộc vào cách gọi hàm. Nếu hàm chứa nó được gọi bởi một đối tượng thì giá trị của biến this được đặt bằng biến đó nếu bạn 'this' không tường minh thì nó giá trị biến This sẽ được đặt bằng Global Object hoặc Undefined (use strict)

Mô tả đơn giản của Lexical Environment


Variable Environment 

VE khá giống với LE hoặc bạn có thể suy nghĩ rằng chúng giống nhau ,có một điểm khác rằng Environment Record của VE  chứa các rằng buộc (binding) được tạo bởi các biến khai báo kiểu statement trong EC này.
Như đã mình đã nói ở trên VE cũng là một LE nên nó có tất cả các thuộc tính và thành phần của LE mà mình đã liệt kê bên trên.

Còn một điều nữa mình muốn nói đó là trong ES6 có điều khác biệt giữa các thành phần của LE và thành phần của VE
- Environment Record (LE) : dùng để lưu trữ function declaration và biến kiểu let const
- Environment Record (VE) : dùng để lưu trữ rằng buộc của biến khai báo kiểu var

Hãy cùng đi vào một ví dụ :

Khi một tệp JS được chạy ban đầu ECG sẽ tự động được khởi tạo và chạy.

Bạn có thể thấy được sự khác biệt giữa LE và VE khi được khơi tạo trong ECG
- Trong LE thì các định dang của biến (tên biến) được khai báo kiểu let và const + hàm declarations được lưu trữ.
- Trong LE thì các định dang của biến (tên biến) được khai báo kiểu var được lưu trữ
- Ở giai đoạn Create Phase thì các biến chưa được gán giá trị mà chúng mới được lưu trữ các định danh trong LE và VE mà thôi nhưng sự khác biệt rằng biến kiểu let sẽ là uninitialized nhưng biến kiểu var sẽ báo undefined .

Execution Phase

Ở giai đoạn Execution Phase JSE sẽ thực thi từng dòng từ đó các biến sẽ được thiết lập các giá trị:

Khi JSE chạy đến dòng Invoked hàm multiply thì một EC mới được tạo ra => quá trình chạy của ECG bị dừng lại đóng băng vì JS chỉ thực thi một tác vụ duy nhất tại một thời điểm, giai đoạn Create Phase của EC (multiply) được diễn ra, nhìn nó sẽ trông giống như thế này.





Nhận xét

Bài đăng phổ biến