Học về Closures của Javascript trong 6 phút

Closures là một thứ khá là khó để nắm bắt trong Javascript. Tuy vậy nó lại rất quan trọng nếu như bạn muốn trở thành một Javascript Developer. 

Việc hiểu rõ về closures sẽ giúp cho code của bạn trở lên “thanh lịch” hơn và vì nó là một thứ quan trọng nên là việc bạn hiểu rõ nó sẽ giúp cho bạn trong các cuộc phỏng vấn về Javascript.

BONUS: Thật ra thì closures không phải là khái niệm riêng của Javascript. Đây là một khái niệm chung trong khoa học máy tính/lập trình vì vậy mà bạn có thể dễ dàng tìm thấy ở mọi nơi, mọi ngôn ngữ lập trình hiện đại.

Functions Are Values Too

Đầu tiên, bạn cần nhiểu rằng là Javascript hỗ trợ first-class functions. (Bạn có thể đọc về first-class function tại đây)

Nghe tên thì có vẻ khá là lạ và cao siêu đúng không. Nhưng nó đơn giản chỉ là function được sử dụng giống như là bất cứ các loại giá trị nào khác. Các loại ở đây là string, number hay object.

Các giá trị được gán vào biến

const name = 'Yazeed';
const age = 25;
const fullPerson = {
    name: name,
    age: age
};

Các giá trị được cho vào array

const items = [
    'Yazeed',
    25,
    { name: 'Yazeed', age: 25 }
]

Các giá trị có thể được trả về từ các function

function getPerson() {
    return [
        'Yazeed',
        25,
        { name: 'Yazeed', age: 25 }
    ];
}

Và đoán xem, function cũng có thể như trên.

Function có thể gán vào biến

const sayHi = function(name) {
    return `Hi, ${name}!`;
}

Function được cho vào array

const myFunctions = [
    function sayHi(name) {
        return `Hi, ${name}!`;
    },
    function add(x, y) {
        return x + y;
    }
];

Và một điều thú vị đây này…

Function có thể được trả về từ các function

Một hàm mà trả về một hàm khác có một tên khá đặc biệt. Nó được gọi là hàm bậc cao (higher-order). Đây chính là nền tảng của closure đó, chúng ta cùng xét ví dụ này nhé:

function getGreeter() {
    return function() {
        return 'Hi, Sunners!';
    };
}

Bạn có thể thấy rằng là getGreeter trả về một function khác bên trong. Vì vậy để được chào đón, hãy gọi nó hai lần như sau:

getGreeter(); // Returns function
getGreeter()(); // Hi, Sunners!

Một là để trả về function bên trong, còn cái sau đó chính là để lấy về giá trị cuối cùng của function bên trong đó.

Bạn cũng có thể dễ dàng khởi tạo nó thành một biến để có thể sử dụng:

const greetJerome = getGreeter();

greetJerome(); // Hi, Jerome!
greetJerome(); // Hi, Jerome!
greetJerome(); // Hi, Jerome!

Kết hợp cùng tham số

Thay vì fix cứng từ ‘Sunners’ thì chúng ta có thể truyền tham số để nó chào người khác.

// We can greet anyone now!
function getGreeter(name) {
    return function() {
        return `Hi, ${name}!`;
    };
}

Và khi sử dụng:

const greetJerome = getGreeter('Jerome');
const greetYazeed = getGreeter('Yazeed');

greetJerome(); // Hi, Jerome!
greetYazeed(); // Hi, Yazeed!

Nhìn vào ví dụ trên bạn có thể thấy rằng param name là của thằng function cha. Tuy vậy thằng function con bên trong cũng có thể sử dụng được nhé. Đây chính là sức mạnh của closures đó.

Lợi ích của việc sử dụng Closures

  1. Data Privacy

Data Privacy chính là việc tránh cho các thành phần bên ngoài thao tác đến các biến bên trong function.

Ví dụ: Ngân hàng không có Data Privacy

Hãy xem xét đoạn code dưới đây là để quản lý một tài khoản ngân hàng.

let accountBalance = 0;
const manageBankAccount = function() {
    return {
        deposit: function(amount) {
            accountBalance += amount;
        },
        withdraw: function(amount) {
            // ... safety logic
            accountBalance -= amount;
        }
    };
}

Tuy vậy accountBalance hoàn toàn có thể bị thay đổi giá trị từ bên ngoài: // later in the script…

accountBalance = 'Whatever I want, muhahaha >:)';

Các ngôn ngữ như Java và C++, PHP,… cho phép các lớp có các property riêng (protected, private) . Các property này không thể được truy cập bên ngoài lớp.

JavaScript không hỗ trợ các biến riêng tư, nhưng chúng ta có thể sử dụng các closures!

const manageBankAccount = function(initialBalance) {
    let accountBalance = initialBalance;
    
    return {
        getBalance: function() { return accountBalance; },
        deposit: function(amount) { accountBalance += amount; },
        withdraw: function(amount) {
            if (amount > accountBalance) {
                return 'You cannot draw that much!';
            }

            accountBalance -= amount;
        }
    };
}

Và có lẽ sử dụng nó như vậy …

const accountManager = manageBankAccount(0);

accountManager.deposit(1000);
accountManager.withdraw(500);
accountManager.getBalance(500);

Và bây giờ thì bất cứ cái gì cũng ko thể thao tác đến accountBalance một cách trực tiếp mà phải thông qua deposit hay là withdraw.

  1. Currying

Currying là gì thì bạn có thể xem chi tiết ở đây nhé. Tìm hiểu Higher-Order Function (HOF) và Currying qua một số ví dụ

Thay vì:

const add = function(x, y) {
    return x + y;
}

add(2, 4); // 6

Bạn có thể chuyển thành currying như sau:

const add = function(x) {
    return function(y) {
        return x + y;
    }
}

Và bây giờ để sử dụng:

const add10 = add(10);

add10(10); // 20
add10(20); // 30
add10(30); // 40

Summary

  • Function có thể dùng như các kiểu dữ liệu khác.
  • Function có thể return function khác.
  • Các biến của hàm cha có thể được sử dụng ở trong các hàm con.
  • Các biến này được gọi là state.

Bài viết được dịch từ: https://www.freecodecamp.org/news/learn-javascript-closures-in-n-minutes/

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *