THÔNG DỤNG
OOP
ES6
ES(X)
CÁC CHỦ ĐỀ
BÀI MỚI NHẤT
Dự án mới của mình là gamehow.net, mời anh em ghé thăm và góp ý ạ.

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.

banquyen png
Bài viết này được đăng tại freetuts.net, không được copy dưới mọi hình thức.

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.

Để 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.

Chế độ bình thường
function sayHi(){
    return function(){
        console.log(this);
    };
}

var msg = sayHi();
msg(); // window object
Chế độ strict mode
'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.

Demo RUN
// 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 đó.

Demo RUN
// 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é.

Cùng chuyên mục:

Cách gộp hai object javascript lại với nhau

Cách gộp hai object javascript lại với nhau

Cách lấy chiều dài của object trong Javascript

Cách lấy chiều dài của object trong Javascript

Hướng dẫn giải phương trình bậc 1 bằng Javascript

Hướng dẫn giải phương trình bậc 1 bằng Javascript

Cách dùng nextSibling trong javascript

Cách dùng nextSibling trong javascript

Cách dùng insertAdjacentHTML trong javascript

Cách dùng insertAdjacentHTML trong javascript

Cách dùng innerHTML trong Javascript

Cách dùng innerHTML trong Javascript

Cách dùng insertBefore trong javascript

Cách dùng insertBefore trong javascript

Cách dùng insertAfter trong Javascript

Cách dùng insertAfter trong Javascript

Cách dùng parentNode trong Javascript

Cách dùng parentNode trong Javascript

Cách dùng parentElement trong Javascript

Cách dùng parentElement trong Javascript

Tính tổng các phần tử trong mảng javascript

Tính tổng các phần tử trong mảng javascript

Tính tổng hai số bằng Javascript (cộng hai số)

Tính tổng hai số bằng Javascript (cộng hai số)

Cách gán giá trị cho thẻ input trong javascript

Cách gán giá trị cho thẻ input trong javascript

Để gán giá trị cho thẻ input thì ta có hai cách, thứ nhất là…

Cách kiểm tra số nguyên âm trong javascript

Cách kiểm tra số nguyên âm trong javascript

Cách kiểm tra số nguyên dương trong javascript

Cách kiểm tra số nguyên dương trong javascript

Biểu thức chính quy RegEx trong Javascript

Biểu thức chính quy RegEx trong Javascript

Bài này chúng ta sẽ tìm hiểu đến chuỗi và cách sử dụng biểu thức…

Cách dùng Import / Export Module trong javascript

Cách dùng Import / Export Module trong javascript

Khi bạn xây dựng một ứng dụng nhỏ thì việc đặt

Cơ chế hoạt động của hoisting trong Javascript

Cơ chế hoạt động của hoisting trong Javascript

Hoisting là vấn đề liên quan đến cách khai báo biến trong Javascript. Nó liên…

Cấp độ private / protected của class trong Javascript

Cấp độ private / protected của class trong Javascript

Cách dùng static trong Javascript (thuộc tính và phương thức tĩnh)

Cách dùng static trong Javascript (thuộc tính và phương thức tĩnh)

Trong bài này chúng ta sẽ tìm hiểu cách sử dụng từ ..

Top