Hướng dẫn what is javascript closure? - javascript đóng cửa là gì?
JavaScript Closures
Closures là gì?Closures là một thuộc tính vô cùng mạnh mẽ của Javascript (và của nhiều ngôn ngữ lập trình khác). Dựa theo định nghĩa từ MDN thì:
Lưu ý: Free variables là các biến không được khai báo local hay được truyền thông qua tham số (parameters). Hãy xem xét các ví dụ sau: Ví dụ 1
Trong ví dụ trên, hàm 2 tạo ra một biến local 3 và 4 (một hàm in ra num trong console). Hàm 4 không có bất kỳ biến local nào trong nó. Tuy nhiên, nó có quyền truy cập vào các biến bên ngoài function, bởi vì 2 là một closure. Do đó, nó có thể sử dụng biến 3 được khai báo trong 2 để log 3 trong console sau khi 2 được trả lại.Ví dụ 2
Chú ý, biến 1 được khai báo sau anonymous function nhưng vẫn có thể truy cập biến 1. Điều này là do biến 1 đã được khai báo trong function scope tại thời điểm được tạo ra, làm cho nó có sẵn khi anonymous function được thực thi.Ngữ cảnh thực thiLà một khái niệm trừu tượng được sử dụng bởi đặc tả ECMAScript theo dõi và đánh giá thời gian chạy của code. Nó có thể là global context trong đó code của bạn chạy lần đầu tiên hoặc khi luồng thực hiện vào một function body. Tại bất kỳ thời điểm nào, chỉ có một Execution Context (ngữ cảnh thực thi) được chạy. Đó là lý do tại sao Javascript là "single threaded" (đơn luồng), nghĩa là một command chỉ có thể được xử lý tại một thời điểm. Thông thường, các trình duyệt duy trì execution context bằng cách sử dụng ngăn xếp. Do đó chúng ta chỉ có thể thêm hoặc xóa các phần tử ở đầu ngăn xếp. Các execution context đang chạy luôn luôn nằm ở mục trên cùng của ngăn xếp. Nó được lấy ra khỏi đầu ngăn xếp khi code của execution context được đánh giá hoàn toàn, cho phép item ở đầu tiên tiếp quản việc chạy execution context. Hơn nữa, chỉ vì một execution context chạy không có nghĩa là nó phải kết thúc trước khi chạy một execution khác. Trường hợp một execution context bị đình chỉ và một execution context khác được chạy. Execution context đình chỉ có thể sao lưu tại thời điểm nó bị tắt. Một execution context khác được đẩy vào stack và trở thành current execution context (bối cảnh thực thi hiện tại). Hãy xem ví dụ thực tế sau để hiểu thêm về vấn đề này
Khi 4 được return, nó được lấy ra khỏi stack và 5 được hồi phục. Mỗi execution context có các state components khác nhau được sử dụng để theo dõi process của code trong execution context đã làm được. Chúng bao gồm:
Closures
Trở lại với ví dụ cấu trúc lồng nhau:
Dựa trên sự hiểu biết của chúng tôi về cách thức hoạt động của môi trường, chúng tôi có thể nói rằng môi trường định nghĩa cho ví dụ ở trên trông giống như thế này (lưu ý, đây là purely pseudocode):
Khi gọi function để test, chúng ta nhận được kết quả là 45. Giá trị được trả về khi ta gọi hàm 5 (bởi vì hàm 8 trả về hàm 5). 5 có thể truy cập vào biến 1 sau khi function 8 trả về bởi vì 3 tham chiếu đến 1 thông qua môi trường bên ngoài. 5 cũng truy cập được biến 6 bởi vì nó là biến global. Đây gọi là 7.Một ví dụ kinh điển về sự nhầm lẫn khi có một vòng lặp for và chúng ta cố gắng liên kết biến counter trong vòng lặp với một function trong nó: Ví dụ 1:Ví dụ 1:
Trở lại với những gì chúng ta vừa học được, chúng ta sẽ dễ dàng nhận ra sự nhầm lẫn ở đây. Đây là những gì mà môi trường trông thấy tại thời điểm vòng lặp kết thúc:
Các giả định không chính xác ở đây là các scope khác nhau trong 5 funtions bên trong mảng 8. Thay vào đó, những gì đang thực sự xảy ra là môi trường (hay context/scope) là giống nhau trong tất cả 5 functions bên trong mảng 8. Vì vậy, mỗi khi biến 0 được tăng nó cập nhật scope - được chia sẻ bởi tất cả các functions. Đó là lý so tại sao bất kỳ functions nào truy cập biến 0 đều trả về 5 ( 0 có giá trị là 5 khi vòng lặp kết thúc).Có một cách để sửa lỗi này, đó là thêm một context bao bọc mỗi lần function lấy execution context/scope của chúng.
Ngoài ra, có một cách tiếp cận thông minh hơn đó là sử dụng 3 thay vì 4. 3 là block-scope và nó bắt buộc tạo mới một định danh mỗi vòng lặp trong vòng lặp for:
Ví dụ 2: Trong ví dụ này, tôi sẽ giới thiệu cách thức mỗi lần gọi một function tạo mội closure riêng biệt.: Trong ví dụ này, tôi sẽ giới thiệu cách thức mỗi lần gọi một function tạo mội closure riêng biệt. 0Trong ví dụ này, chúng ta có thể thấy rằng mỗi lần gọi tới function 6 sẽ tạo mới một closure, cụ thể là 7 hoặc 3. Lời gọi tiếp theo tới mỗi closure function cập nhật lại các biến closure trong chính nó. Điều đó chứng minh rằng các biến trong mỗi closure có thể tiếp tục được sử dụng bởi 9 của 6 sau khi 1 được trả về.Tl;dr
Hy vọng qua bài viết này giúp các bạn hiểu thêm về closure trong Javascript. Bài viết gốcLet's Learn Javascript Closure |