Understand JavaScript : The Scope and The Scope Chain
Scope là gì ?
Scope đề cập đến khả năng truy cập hoặc khả năng hiển thị của một biến. Phần nào của chương trình có quyền truy cập hay thay đổi giá trị biến đó.Tại sao lại cần Scope :
+ Tình bảo mật hạn chế khả năng truy cập của biến, mỗi biến không thể truy cập ở bên ngoài scope của nó.
+ Tránh xung đột gây nhầm lẫn khi đặt nhiều biến cùng một tên trong một file js.
Có các loại scope nào ?
+ Scope global : Bất kì biến nào không nằm trong cặp dấu ngoặc nhọn hoặc không nằm trong hàm nào
+ Scope function : Nằm trong phạm vi một hàm
+ Scope block : Nằm trong phạm vi một cặp dấu ngoặc nhọn
Lexical Scope là gì ?
Lexical scope (hay còn được gọi là static scope) theo định nghĩa chính xác thì nó có nghĩa rằng scope được xác định ngay từ lúc Lexing Time - Thời điểm từ vựng (thời điểm biên dịch) thay vì được xác định trong thời gian chạy hiểu đơn giản nó đề cập đến khả năng truy cập một biến, hàm và đối tượng dựa trên vị trí vật lý của nó, giá trị biến đó của nó sẽ không thay đổi bất kể bạn truy cập nó ở vị trí nào.
Để dễ hiểu hơn chúng ta cùng đi đến một ví dụ.
Trong JS mỗi hàm được tạo ra một scope (phạm vi) nó giống như một không gian hoặc một môi trường mà trong đó các biến định nghĩa trong hàm có thể truy cập được.
Trong nhiều ngôn ngữ lập trình khác thì để tạo một scope bạn có rất nhiều cách như khai báo một statement ifElse hoặc for Loop nhưng trong JS thì để tạo ra một scope bạn chỉ có duy nhất một cách là khai báo một hàm.
Trong JS có một tính chất đó là Lexical Scoping nó đặc trưng cho cái gì đó được viết trong mã , vì vậy đó là về vị trí của một cái gì đó trong code của chúng ta , Lexical scoping cho phép một hàm con có quyền truy cập biến và hàm của các hàm bao bọc nó.
Như mình đã nói ở các bài trước , bất kì hàm nào khi được gọi sẽ được tạo ra Execution Context của riêng nó và EC sẽ được đẩy lên trên cùng của The Execution Context Stack các biến được định nghĩa trong EC này luôn và được chứa bên trong Environtment Context Variable (môi trường biến) và các biến ở các EC khác nhau thì không liên quan đến nhau.
Bạn có thể thấy trong hàm B mình không hề khai báo biến a ..vậy kq của chương trình kia là gì ?
=> Kết quả bạn có thể thấy ở màn hình console là : 1 ??
Vậy tại sao myVar của EC hàm B lại nhận giá trị của biến myVar có phạm vi Global
Hãy cùng xem sơ đồ :
Khi chúng ta thao tác vs một biến ở trong JS sẽ làm nhiều hơn sau đó..Hãy nhớ lại rằng khi EC mới được tạo ra thì sẽ đồng thời tạo ra 3 thứ : this , global object , outer environment..và trong bài này mình sẽ nói đến outer environtment , nói đơn giản thì nó giúp EC có thể tham chiếu đến môi trường bên ngoài của EC mà nó nằm trong hoặc có thể hiểu rằng mỗi EC được tạo ra luôn tham chiếu đến môi trường bên ngoài của nó (outer environment), vậy môi trường bên ngoài (outer environtment) đó là gì và EC sẽ tham chiếu đến Onter Environment như thế nào ??
- Đối với hàm b , môi trường bên ngoài của nó là EC Global , đối với a thì EC Global cũng mang vai trò tương tự.
=> Vậy có những loại môi trường bên ngoài nào ?
Hãy nhớ đến nhưng khái niệm ở đầu seri này nó là Lexical Environment : nó xác định một số thứ nhất định như cách mà JSE sẽ quyết định và giải thích mọi thứ , quyết định mọi thứ sẽ được phân bổ trong bộ nhớ ra sao và cách chúng kết nối với nhau , vậy bạn có thể quan sát đoạn code trên hàm B nằm ở đâu ??
=> Nó được khai báo đầu tiên trên đỉnh của Global Environment , có thể hiểu rằng nó đang nằm cùng cấp với biến myVar =1 do đó nó có thể tham chiếu đến EC global mà nó không thể tham chiếu đến EC của a vì nó không nằm trong EC của a
..Khi bạn yêu cầu một biến trong khi chạy một dòng mã bên trong bất kì một bối cảnh thực hiện cụ thể nào , nếu nó không thể tìm thấy biến đó bên trong EC thì nó sẽ tìm trong Outer Environment.
Outer Environment lại tham chiếu đến EC nào thì sẽ phụ thuộc vào vị trí của hàm nằm trong Lecical Environment ..
=> Vì vậy Hàm b tuy không nằm trong EC global vì hàm này có phạm vi khai báo Global
- Tóm lại : Khi bạn gọi đến một hàm , JSE sẽ tạo một tham chiếu cho Execution Context đó , kiểm tra phạm vi của nó bằng Syntax Parser vì nó đi qua từng dòng code ..sau đó tạo ra các tham chiếu bên ngoài thích hợp dựa trên phạm vi mà hàm của bạn được khai báo .Bây giờ toàn bộ hành động tìm kiếm môi trường bên ngoài mà EC sẽ tham chiếu đến này (bởi vì Execution Context Stack này có thể được xếp bởi rất nhiều EC bao nhiêu lần hàm được gọi thì sẽ có bấy nhiêu Ec được tạo ra từ đó dẫn đến khi chúng ta yêu cầu biến thì JSE sẽ tìm kiếm trong rất nhiều môi trường mà EC tham chiếu đến ) Toàn bộ hành động này được gọi là Scope Chain..
Bây giờ mình sẽ thay đổi Scope của hàm B , đặt nó vào trong hàm a => Lúc này mình đã thay đổi lexical environment của nó dẫn đến outer environment của nó cũng thay đổi do JSE sẽ tham chiếu tới môt trường bên ngoài khác..đầu tiên EC Global được tạo => EC của hàm a => EC của hàm b
rõ ràng lúc này b đang được khai báo vào định nghĩa trong hàm a và tất nhiên JSE sẽ thấy rằng Outer Environment gần nhất của nó là Environment Variable hàm a , nó đi qua lần lượt các Environment gần nhất tìm được biến của environment nào thì nó lấy luôn
Nhận xét
Đăng nhận xét