Thứ năm, 12/12/2019 | 00:00 GMT+7

Giới thiệu về Closures và Currying trong JavaScript

Nếu bạn viết mã bằng JavaScript, rất có thể bạn đã gặp phải thuật ngữ closure , đây là một khái niệm hữu ích nhưng thường gây nhầm lẫn. Nhưng đóng cửa là gì?

Một bao đóng có thể được mô tả là sự kết hợp của một hàm và môi trường từ vựng mà nó được khai báo.

Nhưng chính xác thì điều này nghĩa là gì? Môi trường từ vựng bao gồm bất kỳ biến local nào trong phạm vi của hàm khi hàm được tạo. Một bao đóng cho phép người ta tham chiếu đến tất cả các biến local của một hàm ở trạng thái chúng được tìm thấy.

Điều này về cơ bản đạt được bằng cách xác định một chức năng bên trong một chức năng khác, chức năng này bên trong một chức năng về mặt kỹ thuật là sự đóng lại. Mỗi khi hàm cha được gọi, một bối cảnh thực thi mới được tạo ra, giữ một bản sao mới của tất cả các biến local . Các biến local này có thể được tham chiếu trong phạm vi toàn cục bằng cách liên kết chúng với các biến được khai báo trên toàn cục hoặc trả về bao đóng từ hàm mẹ.

Một ví dụ cơ bản sẽ có định dạng tương tự như sau:

function closuredFunc (){     function closure(){     // some logic     } } 

Cũng có thể có một bao đóng trả về một số phương thức như được hiển thị bên dưới:

function closure(){     function first() { console.log('I was declared first')}     function second() { console.log('I was declared second')}     function third() { console.log('I was declared third')}     return [first, second, third] } 

Để tham chiếu đến từng phương thức này, ta sẽ gán bao đóng của ta cho một biến toàn cục, sau đó sẽ trỏ đến một mảng các phương thức được hiển thị. Mỗi phương thức sau đó có thể được gán cho các tên biến duy nhất để đưa chúng vào phạm vi toàn cục như hình dưới đây. Đến đây, chúng có thể được gọi.

let f = closure()  let one = f[0] let two = f[1] let three = f[2]  one() // logs I was declared first two() // logs I was declared second three() // logs I was declared third 

Tại sao sử dụng Closures?

Bạn có thể tự hỏi tại sao một người lại gặp khó khăn khi đóng cửa. Chà, đóng cửa có một số công dụng và lợi thế.

Trước khi giới thiệu Lớp trong ES6, các bao đóng cung cấp một phương tiện tạo quyền riêng tư giống như lớp tương tự như được sử dụng trong Lập trình hướng đối tượng, cho phép ta mô phỏng các phương thức riêng. Đây được gọi là module pattern và nó cho phép ta viết mã dễ bảo trì với việc giảm ô nhiễm không gian tên và nhiều khả năng tái sử dụng hơn.

Hãy xem xét một trường hợp mà điều này được thực hiện:

var makeCounter = function() {   var privateCounter = 0;   function changeBy(val) {     privateCounter += val;   }   return {     increment: function() {       changeBy(1);     },     decrement: function() {       changeBy(-1);     },     value: function() {       return privateCounter;     }   } };  var counter1 = makeCounter(); var counter2 = makeCounter();  counter1.value(); // returns 0 counter1.increment(); // adds 1 counter1.increment(); // adds 1 counter1.value(); // returns 2 counter1.decrement(); //subtracts 1 counter1.value(); // returns 1 counter2.value(); // returns 0 

Trong ví dụ trên, ta đã khai báo một hàm makeCounter là một hàm công khai có quyền truy cập vào một số biến private bên trong nó như privateCounter và các hàm thao tác với nó. Điều này bắt chước hành vi tạo makeCounter dưới dạng một lớp với chính nó được xây dựng trong fuctionality và biến. Có thể thấy điều này khi ta tạo hai bộ đếm khác nhau, bộ counter1 và bộ counter2 . Mỗi bộ đếm độc lập với bộ đếm khác và tham chiếu đến một version khác nhau của các biến.

Closures cũng cho phép ta sử dụng các hàm để tạo các hàm khác bổ sung một giá trị cụ thể cho đối số của chúng. Trong trường hợp này, hàm cha cho phép hành vi này được gọi là một function factory vì nó về cơ bản tạo ra các hàm khác.

Sử dụng các nhà máy chức năng, ta có thể đạt được một hành vi được gọi là Currying , mà ta sẽ đề cập trong phần tiếp theo.

Cà ri là gì

Currying là mô hình của các chức năng ngay lập tức đánh giá và trả về các chức năng khác. Điều này được thực hiện bởi thực tế là các hàm Javascript là các biểu thức có thể trả về các hàm khác.

Các hàm có cấu trúc được xây dựng bằng cách đóng chuỗi bằng cách xác định và trả về đồng thời các hàm bên trong của chúng ngay lập tức.

Đây là một ví dụ về món cà ri:

let greeting = function (a) {     return function (b) {         return a + ' ' + b     } }  let hello = greeting('Hello') let morning = greeting('Good morning')  hello('Austin') // returns Hello Austin hello('Roy') // returns Hello Roy morning('Austin') // returns Good morning Austin morning('Roy') //returns Good Morning Roy 

Hai hàm được tạo từ greeting ( hellomorning ), mỗi hàm trả về sẽ xử lý các đầu vào được cung cấp để tạo ra một câu chào. Họ cũng có một đối số là tên của người được chào đón.

Trong trường hợp trên, câu chào cũng được sử dụng như một nhà máy chức năng với hai chức năng hello và morning được tạo ra từ nó.

Hàm bên trong cũng có thể được gọi sau lần gọi đầu tiên như sau:

 greeting('Hello There')('General Kenobi')  //returns Hello There General Kenobi 

Currying được coi là một phần của lập trình hàm và vì vậy các hàm curry như vậy có thể dễ dàng được viết bằng cách sử dụng cú pháp hàm mũi tên trong ES6 và các version Javascript mới hơn để có mã rõ ràng, thanh lịch hơn:

 let greeting = (a) => (b) => a + ' ' + b   greeting('Hello There')('General Kenobi')  //returns Hello There General Kenobi 

Kết luận

Mặc dù các bao đóng có thể không được sử dụng phổ biến vì các lớp được kết hợp trong Javascript trong ES6, nhưng chúng vẫn có vị trí của bạn khi nói đến việc viết mã sạch có thể tái sử dụng. Closurescurrying cũng là những khái niệm quan trọng cần hiểu khi nói đến lập trình chức năng, nơi chúng về cơ bản phục vụ một mục đích tương tự như các phương thức riêng trong Lập trình hướng đối tượng.


Tags:

Các tin liên quan

Cách gói một gói JavaScript Vanilla để sử dụng trong React
2019-12-12
Cách phát triển một trình tải lên tệp tương tác với JavaScript và Canvas
2019-12-12
Cách sử dụng map (), filter () và Reduce () trong JavaScript
2019-12-12
Giải thích về lập trình chức năng JavaScript: Ứng dụng một phần và làm xoăn
2019-12-12
Bắt đầu với các hàm mũi tên ES6 trong JavaScript
2019-12-12
Cách đếm số nguyên âm trong một chuỗi văn bản bằng thuật toán JavaScript
2019-12-12
Cách sử dụng phép gán cấu trúc hủy trong JavaScript
2019-12-12
Giải thích về lập trình chức năng JavaScript: Kết hợp & truyền tải
2019-12-12
Cách sử dụng .every () và .some () để điều khiển mảng JavaScript
2019-12-12
Chuyển đổi Mảng sang Chuỗi trong JavaScript
2019-12-05