Closures, một thuật ngữ quan trọng trong lĩnh vực công nghệ thông tin, đang rộng rãi được ứng dụng. Nó không chỉ đóng vai trò quan trọng trong quá trình đánh giá việc tăng lương của bạn mà còn ảnh hưởng đến sự phát triển nhanh chóng trong sự nghiệp của những người làm phần mềm. Việc hiểu rõ về Closures không chỉ giúp bạn nắm bắt được một khái niệm quan trọng mà còn mở ra cơ hội cho việc thăng tiến nhanh chóng trong môi trường làm việc.
Trong môi trường công nghiệp ngày nay, Closures không chỉ là một khái niệm trừu tượng mà còn là một yếu tố quyết định sự thành công cá nhân trong lĩnh vực lập trình và phát triển phần mềm. Việc áp dụng Closures đúng cách không chỉ làm cho mã nguồn của bạn trở nên hiệu quả và dễ bảo trì hơn mà còn mở ra cánh cửa cho những cơ hội mới.
Chúng ta không thể phủ nhận vai trò quan trọng của Closures trong quá trình đánh giá năng lực và giá trị cá nhân của mỗi lập trình viên. Việc hiểu rõ về cách Closures hoạt động và ứng dụng chúng trong dự án cụ thể không chỉ là một kiến thức chuyên sâu mà còn là một kỹ năng quan trọng giúp bạn nổi bật trong đám đông.
Vì vậy, để có sự phát triển mạnh mẽ trong sự nghiệp công nghệ thông tin, hãy dành thời gian để tìm hiểu và áp dụng Closures. Điều này không chỉ làm tăng khả năng đạt được sự công nhận mà còn làm cho bạn trở thành một nguồn lực quan trọng trong đội ngũ phát triển.
Định nghĩa Closure là gì?
Closure là một khái niệm trong lập trình được định nghĩa như việc một hàm được viết lồng vào bên trong một hàm khác. Điều đặc biệt là closure có khả năng sử dụng và biến đổi cả biến toàn cục và biến cục bộ của chính nó.
Trong ngôn ngữ lập trình Javascript và nhiều ngôn ngữ khác, closure được coi là một tính năng mạnh mẽ. Một cách đơn giản để hiểu closure trong Javascript là xem nó như một tập hợp, bao gồm môi trường nơi mà một hàm đã được khai báo và chính hàm đó. Môi trường này bao gồm các biến cục bộ trong phạm vi của hàm đã được khai báo, và closure có thể sử dụng những biến này ngay cả sau khi hàm bên ngoài đã hoàn thành thực thi.
Định nghĩa JavaScript Closures có thể được mô tả như sau: JavaScript Closures là một dạng tập hợp, kết hợp môi trường nơi một hàm đã được khai báo và chính hàm đó. Môi trường này chứa các biến cục bộ đã được khai báo trong phạm vi của hàm. Sức mạnh của closure là khả năng giữ lại và sử dụng các giá trị của biến ngay cả sau khi hàm bên ngoài đã kết thúc thực thi, tạo ra sự linh hoạt và tính tái sử dụng trong mã nguồn.
Định nghĩa Closure là gì?
Hàm closures có khả năng truy cập biến số ở 3 phạm vi khác nhau như sau:
- Biến dạng toàn cục.
- Biến đã được khai báo ở hàm số có chứa sẵn hàm closures (outer function)
- Biến nằm ở trên hoặc trong hàm closures
Closure được trông như thế nào nhỉ?
Phía trên là cách viết phổ biến và bạn chỉ cần viết lại như sau:
closurefunction init() {
var name = “Closure”; function hi() { console.log(‘my name is ‘ + name); } return hi; // lưu ý không viết là return hi() } var f = init(); f(); |
Function trong JavaScript không chỉ là một loại dữ liệu mà còn đặc biệt hơn so với các loại khác như string, number, v.v. ở điểm là nó có khả năng thực thi (executable). Do đó, function có thể được sử dụng để trả về giá trị thông qua câu lệnh return, tương tự như các loại dữ liệu khác.
Trong JavaScript, mọi đối tượng thường có type là function đều có thể thực thi bằng cách sử dụng cặp dấu ngoặc () và sau đó là cách gọi hàm, được biết đến như là f().
Một điều đặc biệt là trong JavaScript, khi một hàm được trả về từ một hàm khác (closure), các biến local của nó không bị thu hồi như trong Java, C hoặc C++. Trong trường hợp này, khi một hàm con được return từ một hàm khác, môi trường nơi hàm con được tạo ra sẽ được kết nối với hàm con dưới dạng một reference, giống như việc sử dụng con trỏ trong C++.
Khi gọi hàm f(), biến name vẫn tồn tại vì nó được giữ lại từ môi trường của hàm init. Trên tab console của Chrome Developer Tools, khi thực hiện console.dir(f), bạn có thể thấy một mảng Scopes với 2 phần tử là Closure và Global. Điều này chứng tỏ rằng Closure đó là phạm vi local nơi biến name vẫn tồn tại, có giá trị là “Closure”.
Closure được sử dụng để tái hiện lại bản hack
Chính vì vậy, lý do mà bạn cần phải gắn kèm với reference của ENV dựa theo closure là khả năng chạy được bất đồng bộ của JS. Ngay sau khi return hi hàm init đã được hoàn tất, từ đó các biến local đều sẽ bị thu hồi và trong số đó có name.
Để đảm bảo cho hi có thể chạy được thì điều cần thiết cũng là cách đơn giản nhất bạn cần làm là chụp lại ảnh ENV – trong số đó cần phải chứa name- từ đó gửi kèm theo hi ở dưới dạng một reference. Ở đây, reference là một trong cách cách giúp giải quyết hiệu quả về năng suất của bộ nhớ (nếu như bạn từng dùng Prototype thì nó khá quen thuộc).
Tìm hiểu về hàm closures lưu trữ biến số của outer function theo dạng tham chiếu
Hàm objId trả về một đối tượng có chứa hai hàm closures: getId và setId. Cả hai hàm này sử dụng chung một biến cục bộ được đặt tên là id.
Khi gọi myObject.getId(), giá trị trả về là một biến cục bộ. Nếu sau đó bạn thực hiện myObject.setId(10), và nếu hàm closures chỉ lưu giá trị của biến cục bộ theo kiểu giá trị, thì giá trị của biến cục bộ id sẽ không thay đổi.
Khi gọi myObject.getId() sau đó, giá trị trả về sẽ vẫn là 10. Điều này chứng tỏ rằng hàm closures cần phải lưu trữ biến cục bộ theo kiểu tham chiếu để có thể theo dõi các thay đổi.
Trên tất cả, đây là một trong những cách tạo đối tượng trong JavaScript, sử dụng hàm closures để duy trì và quản lý trạng thái của biến cục bộ id.
Các ứng dụng thực tế của Closure là gì?
Để nắm rõ được ứng dụng của Closure là gì thì trước tiên bạn hãy cùng ITNavi xem xét một số ví dụ thực tế của closure như sau: Ví dụ sẽ bao gồm 1 trường hợp có cùng function nhưng khác ENV và đều có cùng ENV nhưng khác function.
Closure là 1 trong những yếu tố quan trọng với lập trình viên
Trường hợp 1: Function factory
Ví dụ như sau:
function makeExponentiation(x) { var exponent = x;
return function(y) { return Math.pow(y, exponent); } } var sqr = makeExponentiation(2); var sqrt = makeExponentiation(0.5); console.log(‘3 bình phương là ‘ + sqr(3)); console.log(‘căn bậc hai của 9 là ‘ + sqrt(9)); |
Với ví dụ trên thì bạn có thể thấy rằng, hàm makeExponentiation trông giống như một function factory và nó có thể tạo ra được các function khác tùy vào tham số được truyền vào. Trong đó, sqr và sqrt đều là 2 closure sở hữu body giống nhau nhưng ENV đều khác nhau.
Nếu như, ENV của sqr có chứa {exponent: 2} thì của sqrt sẽ chứa {exponent: 0.5}. ENV của mỗi một closure thì chỉ chứa các biến hay hàm mà nó đã được sử dụng, ở đây thì nó được xem là biến exponent. Với việc, ENV có chứa tất cả những biến local và outer lại rất thừa thãi cũng như không mang lại hiệu quả về mặt hiệu năng.
Trường hợp 2: Mô phỏng lại phạm vi của biến trong OOP (variable visibility)
Bên trong JS sẽ không có bất kỳ khái niệm Class nào được sử dụng đúng nghĩa như trong C++. Từ đó, khái niệm Class trong ES6 chỉ là cách khai báo và một cách hack. Sử dụng closure mô phỏng lại cách hack này như sau:
function Counter() { var counter = 0;
function add(number) { counter += number; } return { increment: function() { add(1); }, decrement: function() { add(-1); }, value: function() { return counter; } }; }); var counter = Counter(); console.log(‘giá trị ban đầu ‘ + counter.value()); counter.increment(); counter.increment(); console.log(‘sau khi tăng 2 lần ‘ + counter.value()); counter.decrement(); console.log(‘sau khi giảm 1 lần ‘ + counter.value()); |
Mỗi hàm increment, decrement và value đều là một closure với các body khác nhau, nhưng chúng chia sẻ cùng một environment (ENV). Việc chia sẻ ENV là một điểm quan trọng để mô phỏng Class trong JavaScript. Khi một closure cập nhật một biến, sự thay đổi này được phản ánh tại các closure khác có chung ENV. Tất cả các closure này đều thao tác trên một tập hợp biến tương tự.
Ngoại trừ class Counter, không còn cách nào truy cập trực tiếp các biến counter của nó. Tuy nhiên, bạn có thể thay đổi giá trị counter thông qua các hàm public như increment và decrement. Hành vi này mô phỏng một cách gần đúng một Class trong các ngôn ngữ lập trình khác như PHP hoặc Java.
Tổng kết
Dĩ nhiên rồi, sau khi đọc những chia sẻ từ Blog ở trên, bạn đã có một hiểu biết khá rõ về khái niệm Closure, phải không? Hiện nay, Closure đóng vai trò quan trọng trong việc đánh giá khả năng phát triển của các lập trình viên trong tương lai. Do đó, đối với mỗi nhà phát triển, việc nắm vững thông tin liên quan đến Closure là hết sức quan trọng để nâng cao khả năng tiến xa trong sự nghiệp.
Điều này càng thể hiện tầm quan trọng khi kiến thức về Closure không được đào tạo đầy đủ, và do đó, việc tự mình cập nhật kiến thức là không thể thiếu. Nếu bạn đang có ý định phát triển sâu sắc kiến thức về Closure, hãy không quên dành thời gian đều đặn để tự rèn luyện và cập nhật kiến thức của mình trong lĩnh vực này. Điều này sẽ giúp bạn nâng cao khả năng và sẵn sàng đối mặt với những thách thức mới trong sự nghiệp lập trình của mình.