Hàm closure trong javascript
Trong bài này chúng ta sẽ tìm hiểu hàm closure trong javascript, đây là một cách tạo hàm khá hay mà chắc hẳn các bạn đã gặp rất nhiều trong quá trình làm việc với javascript.
Closure function là một khái niệm không phải ai cũng biết và thực sự hiểu về nó. Đây được xem là một cách định nghĩa hàm giúp code nhìn trong sáng và sử dụng linh hoạt hơn. Vì vậy, nếu bạn muốn học javascript nâng cao hoặc học các framework khác thì phải hiểu về closure.
Vậy closure là gì? Cách tạo closure như thế nào? Khi nào thì sử dụng hàm closure? Chúng ta cùng tìm hiểu ngay nhé.
1. Closure javascript là gì?
Closure là một hàm được tạo ra từ bên trong một hàm khác, nó có thể sử dụng các biến toàn cục, biến cục bộ của hàm cha và biến cục bộ của chính nó. Việc viết hàm theo kiểu closure trong một số trường hợp sẽ giúp code nhìn sáng và dễ quản lý hơn, linh hoạt hơn trong một số trường hợp.
Bài viết này được đăng tại [free tuts .net]
Để hiểu được hàm closure trong javascript thì bạn phải hiểu được khái niệm về phạm vi của một biến scope. Scope là phạm vi của biến, nơi mà biến tồn tại, hay nói chính xác hơn đó là nơi mà biến sinh ra và chết đi, và chỉ những chương trình nằm trong phạm vi đó mới sử dụng được biến.
Khi bạn tạo một biến trong một hàm, thì phạm vi hoạt động của biến đó chỉ ở bên trong hàm đó mà thôi. Sau khi gọi xong thì biến sẽ bị hủy, và như vậy là kết thúc một vòng đời.
Khi khai báo biến với từ khóa var thì phạm vi của nó rộng hơn rất nhiều so với từ khóa let. Biên let chỉ tồn tại trong phạm vi bắt đầu bằng
{
và kết thúc bằng}
(ta hay gọi là block scoped), tức mức hoạt động cao nhất của nó là cục bộ. Còn biến var thì khác, nó có thể là biến toàn cục hoặc cục bộ.Bạn có thể tham khảo bài viết từ khóa let trong javascript để hiểu rõ hơn về nó.
Quay trở lại vấn đề chính của bài viết này, đó là closure trong js. Theo khái niệm của nó thì chúng ta chỉ việc tạo một hàm nằm bên trong một hàm khác thì đó chính là closure.
Ví dụ dưới đây mình tạo một hàm sayHi()
:
function sayHi(name){ let say = function(){ alert("Xin chào, tôi là " + name); }; return say; }
Trong ví dụ này thì hàm say()
chính là một closure function. Bên trong nó có thể sử dụng được biến của hàm cha name
.
Bạn cũng có thể return về luôn thay vì đặt tên cho hàm closure đó.
function sayHi(name){ return function(){ alert("Xin chào, tôi là " + name); }; }
Bây giờ mình sẽ gọi đến hàm sayHi
, và gán nó vào biến tên là cuong
. Sau đó mình log biến cuong
này xem nó là gì nhé.
var cuong = sayHi("Cường"); console.log(cuong);
Kết quả trả vè nó là một function như sau:
ƒ (){ alert(message); }
Lý do khá đơn giản, bởi vì trong hàm sayHi
mình return về một function, vì vậy biến cuong
chính là function mà mình đã return đó. Để in ra thông báo thì chúng ta phải gọi kích hoạt function này.
var cuong = sayHi("Cường"); cuong(); // Kết quả: Xin chào, tôi là cường
Như vậy là bạn đã hiểu hàm closure trong javascript rồi phải không nào? Bây giờ mình sẽ đi tiếp phần 2 để nói về cách sử dụng biến cha trong closure function.
2. Closure trong javascript với biến của hàm cha
Trước khi đi vào vấn đề chính thì mình xin nhắc lại hai lưu ý sau:
- Các biến bên trong hàm sẽ kết thúc khi hàm đó được chạy xong.
- Closure có thẻ sử dụng biến cục bộ, biến hàm cha và biến trong chính hàm đó.
Bây giờ hãy tạo cho mình một function như sau:
function counter(){ var count = 1; return function(){ return count++; }; }
Hàm counter trả về một closure function, function đó có nhiệm vụ là tăng biến counter
lên 1 đơn vị và trả kết quả về.
Mình sẽ gọi đến hàm counter như sau:
var c = counter();
Nếu theo quy tắc trên thì hàm counter()
đã được chạy xong, và biến count
bên trong hàm này đương nhiên là sẽ bị hủy. Tuy nhiên, nó vẫn còn tồn tại bên trong closure function nhé các bạn, bằng cách thực thi hàm c()
thì ta sẽ nhận được giá trị của biến count.
var c = counter(); console.log(c()); // Kết quả: 1
Mình sẽ gọi hàm c()
thêm vài lần nữa.
var c = counter(); console.log(c()); // Kết quả: 1 console.log(c()); // Kết quả: 2 console.log(c()); // Kết quả: 3 console.log(c()); // Kết quả: 4
Vậy, hàm closure có thể sử dụng các biến của hàm cha mặc dù hàm cha đã chạy xong.
3. Con trỏ this trong closure function
Cũng như những function khác, nếu bạn đang chạy chế độ strict mode thì con trỏ this sẽ là undefined, còn không thì nó là đối tượng window.
function sayHi(){ return function(){ console.log(this); }; } var msg = sayHi(); msg(); // window object
'use strict'; function sayHi(){ return function(){ console.log(this); }; } var msg = sayHi(); msg(); // Undefined
4. Closure trong class javascript
Bạn có thể tạo một closure trong các phương thức của class. Tuy nhiên, vì this
trong closure là undefined nên bạn không thể truy cập đến các thuộc tính của class.
Có một mẹo khá đơn giản, đó là bạn tạo một biến và gán nó chính bằng con trỏ this ở trong các phương thức của classs.
class Student{ constructor(name){ this.name = name; } showName(){ // Đặt một cái tên khác cho this let obj = this; return function(){ console.log("Xin chào, tôi là " + obj.name); }; } } var student1 = new Student("Cường"); var cuong = student1.showName(); cuong();
5. Một vài ví dụ về closure trong javascript
Tới đây thì chắc hẳn các bạn đã hiểu được khái niệm closure javascript là gì rồi phải không nào? Bây giờ ta sẽ thực hành thông qua một vài ví dụ, mỗi ví dụ là một trường hợp mà chắc chắn sau này bạn sẽ gặp khi làm việc với javascript.
Closure có tham số
Trong ví dụ các phần trên trên mình tạo một closure không có tham số. Vậy nếu trường hợp có tham số thì cách viết như thế nào? Bạn xem ví dụ dưới đây sẽ hiểu.
// Bước 1: Tạo hàm closure function showMessage(message) { return function(time){ for (var i = 1; i <= time; i++){ alert(message + ' - Freetuts.net'); } }; } // Bước 2: khởi tạo hàm closure var messageFunc = showMessage('Xin chào các bạn'); // Bước 3: Chạy hàm closure messageFunc(2);
Trong ví dụ này thì hàm showMessage
đã trả về một hàm closure, hàm này có một tham số time
.
Return nhiều closure function
Nếu bạn muốn return nhiều hàm closure thì bạn phải sử dụng một object, trong đó mỗi phần tử sẽ là một closure function.
function multiClosure() { return { func1 : function(){ console.log('Closure1'); }, func2 : function(){ console.log('Closure2'); } }; } // Cách sử dụng var object = multiClosure(); object.func1(); object.func2();
Closure thay đổi giá trị biến toàn cục lẫn cục bộ
Closure có thể sử dụng biến ở 3 phạm vi: Thứ nhất là biến toàn cục, thứ hai là biến của hàm cha và thứ ba là biến của chính nó.
Không chỉ sử dụng được mà nó còn có khả năng thay đổi giá trị của các biến đó.
// Bước 1: Tạo hàm closure function Student() { var name = ''; var age = ''; return { set : function(in_name, in_age){ name = in_name; age = in_age; }, getName : function(){ return name; }, getAge : function(){ return age; } }; } // Bước 2: khởi tạo hàm closure var studentObj = Student(); // Bước 3: Chạy hàm closure studentObj.set('Nguyễn Văn Cường', '27'); alert(studentObj.getName()); // Nguyễn Văn Cường alert(studentObj.getAge()); // 27
6. Độ ưu tiên các biến trong closure function
Như ta biết thì closure có thể sử dụng biến tại ba vị trí, đó là biến toàn cục, biến hàm cha và biến của chính nó.Giả sử tên các biến ở ba vị trí đó bị trùng nhau độ ưu tiên sẽ được sắp xếp như thế nào?
Trường hợp này nó sẽ ưu tiên từ trong ra ngoài như sau:
- Bước 1: Xem biến có nằm trong closure function không? Nếu không thì nhảy qua bước 2, nếu có thì sử dụng.
- Bước 2: Xem biến có nằm trong hàm cha không? Nếu không thì qua bước 3, nếu có thì sử dụng.
- Bước 3: Xem có phải là biến cục bộ không? Nếu có thì sử dụng, nếu không thì nó sẽ khởi tạo biến mới mới.
Xem ví dụ sau:
// Bước 1: Tạo hàm closure var message = 'Biên toàn cục'; function showMessage() { var message = 'Biến cục bộ của hàm cha'; return function(){ alert(message); }; } // Bước 2: khởi tạo hàm closure var messageFunc = showMessage(); // Bước 3: Chạy hàm closure messageFunc();
Trong ví dụ này thì biến message
trong hàm closure chính là biến của hàm cha.
7. Lời kết
Như vậy là chúng ta đã tìm hiểu xong khái niệm closure function trong javascript. Qua bài này hi vọng bạn đã hiểu và co thể trả lời trong các kì phỏng vấn sau này.
Hãy luôn nhớ rằng closure là một khái niệm rất quan trọng, được sử dụng rất nhiều khi làm việc với javascript. Và nếu thấy bài viết hay thì hãy chia sẻ để ủng hộ cho mình nhé.