Understand JavaScript - Closures (P1)
Đã đến lúc cho một chủ đề mang đến nỗi khiếp sợ cho bất cứ DEV JS mới nhập môn nào đó là Closures Đây là chủ đề rất quan trọng và rất khó hiểu mọi kiến thức trong bài viết này đều có liên quan mật thiết đến Execution Context , First Class Function , Execution Context Stack...
Để thấy được sức mạnh của Closures chúng ta cùng quan sát một đoạn code
Trong một hàm thay vì return một string , number mình trả về một hàm , đơn giản thì nó cũng chỉ là một giá trị mà thôi.
Kết quả :
Hoặc một cách viết khác , hàm trả về một hàm , đơn giản thì mình lưu hàm này lại trong biến để có thể gọi lại thôi.
Kết quả vẫn tương tự , bạn có thể thấy rằng khi hàm greet được gọi biến whattosay được khỏi tạo
hàm được thực hiện và nó kết thúc , nó đã hoàn thành công việc và bật ra khỏi Execution Context Stack. vậy mà khi mình gọi hàm say nó vẫn có thể truy cập được giá trị của biến whattosay ????
Bạn có thấy lạ không ? Đó là vì hiện tượng Closure xảy ra . Chúng ta hãy cùng phân tích chi tiết từng giai đoạn chạy của chương trình.
- Khi chương trình chạy EC Global được tạo ra
- Chạy đến dòng số 9 hàm greet được gọi một EC mới được tạo ra và biến whattosay được đặt vào trong môi trường biến của EC , sau đó nó tạo ra một hàm rồi lập tức trả về một hàm đó.
- EC của hàm Greet bị bật ra khỏi Execution Context Stack
Chúng ta đã nói rằng mỗi EC đều có một vùng nhớ riêng nơi đặt các biến của nó (Variable Environment) . Vậy điều gì xảy ra khi EC bị biến mất
=> Trong trường hợp bình thường JSE sẽ xóa nó bằng một trình thu gom rác nhưng thời điểm mà EC chạy xong và bị bật ra khỏi Execution Context Stack thì Variable Environment vẫn còn đó , điểu bạn cần nhớ rằng mặc dù EC đã bị đẩy ra khỏi ECS và bị xóa đi nhưng Variable Environmentcủa nó thì không bị xóa đi, nói chính xác hơn thì những biến mà hàm con (inner) vẫn tham chiếu đến thì sẽ không bị xóa đi mà thôi..nhưng biến không được tham chiếu ở bất kỳ đâu sẽ bị xóa.
Khi hàm sayHi được gọi => Ec mới tạo ra => biến Tony sẽ được lưu vào trong vùng nhớ của EC
=> Nhưng khi mình console.log yêu cầu biến whattosay thì JSE sẽ làm gì ? => JSE sẽ tìm biến whattosay , đi lên theo chuỗi Scope chain bằng cách tham chiếu đến outer environment để tìm biến đó (Vì nó không thể tìm thấy nó bên trong thân hàm) .
Mặc dù EC của hàm greet đã bị bật ra khỏi ECS , việc thực thi sayHi , EC của sayHi vẫn tham chiếu đến các biến , không gian nhớ của Outer Environment của nó .
Nói đơn giản hơn rằng mặc dù hàm greet đã hoàn thành , bất kì hàm nào tạo ra bên trong nó (hàm con của nó) chúng vẫn có khả năng truy cập (tham chiếu) đến Variable Environment của hàm greet .
EC hàm greet đã mất nhưng những gì trong vùng nhớ cho EC của hàm greet không bị mất đi và JSE vẫn đảm bảo rằng hàm của mình vẫn có thể đi theo chuỗi scope chain và tìm các biến được yêu cầu
Và theo cách này chúng ta nói rằng EC hàm sayHi đã Closure các biến nằm ngoài EC của nó.
Closure không phải là thứ mà bạn muốn JSE thực hiện nó đơn giản chỉ là một tính năng của ngôn ngữ này.JSE luôn đảm bảo bất kì hàm nào đang chạy luôn có quyền truy cập vào các biến mà nó được cho là có quyền truy cập.
=> Đây là một tính năng của JS cực kì quan trọng và mạnh mẽ , tóm lại một Closure được hình thành khi chúng ta lồng các hàm , các hàm được lồng bên trong hàm khác hàm nói đơn giản thì là con được hàm cha bao bọc , các hàm con có thể tham chiếu đến các biến có trong hàm cha của nó ngay cả sau khi hàm cha của nó được thực thi.
Để thấy được sức mạnh của Closures chúng ta cùng quan sát một đoạn code
Trong một hàm thay vì return một string , number mình trả về một hàm , đơn giản thì nó cũng chỉ là một giá trị mà thôi.
Kết quả :
Hoặc một cách viết khác , hàm trả về một hàm , đơn giản thì mình lưu hàm này lại trong biến để có thể gọi lại thôi.
Kết quả vẫn tương tự , bạn có thể thấy rằng khi hàm greet được gọi biến whattosay được khỏi tạo
hàm được thực hiện và nó kết thúc , nó đã hoàn thành công việc và bật ra khỏi Execution Context Stack. vậy mà khi mình gọi hàm say nó vẫn có thể truy cập được giá trị của biến whattosay ????
Bạn có thấy lạ không ? Đó là vì hiện tượng Closure xảy ra . Chúng ta hãy cùng phân tích chi tiết từng giai đoạn chạy của chương trình.
- Khi chương trình chạy EC Global được tạo ra
- Chạy đến dòng số 9 hàm greet được gọi một EC mới được tạo ra và biến whattosay được đặt vào trong môi trường biến của EC , sau đó nó tạo ra một hàm rồi lập tức trả về một hàm đó.
- EC của hàm Greet bị bật ra khỏi Execution Context Stack
=> Trong trường hợp bình thường JSE sẽ xóa nó bằng một trình thu gom rác nhưng thời điểm mà EC chạy xong và bị bật ra khỏi Execution Context Stack thì Variable Environment vẫn còn đó , điểu bạn cần nhớ rằng mặc dù EC đã bị đẩy ra khỏi ECS và bị xóa đi nhưng Variable Environmentcủa nó thì không bị xóa đi, nói chính xác hơn thì những biến mà hàm con (inner) vẫn tham chiếu đến thì sẽ không bị xóa đi mà thôi..nhưng biến không được tham chiếu ở bất kỳ đâu sẽ bị xóa.
Khi hàm sayHi được gọi => Ec mới tạo ra => biến Tony sẽ được lưu vào trong vùng nhớ của EC
=> Nhưng khi mình console.log yêu cầu biến whattosay thì JSE sẽ làm gì ? => JSE sẽ tìm biến whattosay , đi lên theo chuỗi Scope chain bằng cách tham chiếu đến outer environment để tìm biến đó (Vì nó không thể tìm thấy nó bên trong thân hàm) .
Mặc dù EC của hàm greet đã bị bật ra khỏi ECS , việc thực thi sayHi , EC của sayHi vẫn tham chiếu đến các biến , không gian nhớ của Outer Environment của nó .
Nói đơn giản hơn rằng mặc dù hàm greet đã hoàn thành , bất kì hàm nào tạo ra bên trong nó (hàm con của nó) chúng vẫn có khả năng truy cập (tham chiếu) đến Variable Environment của hàm greet .
EC hàm greet đã mất nhưng những gì trong vùng nhớ cho EC của hàm greet không bị mất đi và JSE vẫn đảm bảo rằng hàm của mình vẫn có thể đi theo chuỗi scope chain và tìm các biến được yêu cầu
Và theo cách này chúng ta nói rằng EC hàm sayHi đã Closure các biến nằm ngoài EC của nó.
Closure không phải là thứ mà bạn muốn JSE thực hiện nó đơn giản chỉ là một tính năng của ngôn ngữ này.JSE luôn đảm bảo bất kì hàm nào đang chạy luôn có quyền truy cập vào các biến mà nó được cho là có quyền truy cập.
=> Đây là một tính năng của JS cực kì quan trọng và mạnh mẽ , tóm lại một Closure được hình thành khi chúng ta lồng các hàm , các hàm được lồng bên trong hàm khác hàm nói đơn giản thì là con được hàm cha bao bọc , các hàm con có thể tham chiếu đến các biến có trong hàm cha của nó ngay cả sau khi hàm cha của nó được thực thi.

Nhận xét
Đăng nhận xét