Cảm ơn bạn!
Closure trong Javascript là gì? Tìm hiểu về Closure thông qua ví dụ đơn giản
Hi, hôm nay chúng ta sẽ tìm hiểu về khái niệm closure
trong JavaScript nhé. Đây là một khái niệm có thể khiến cho một số bạn cảm thấy khó hiểu.
Trước khi chúng ta tìm hiểu về closure
trong JavaScript. Mình đã viết một số bài liên quan đến closure
đó là scope
và lexical scope
. Các bạn nên đọc bài viết về 2 khái niệm này nếu chưa hiểu chúng là gì 😁.
Bài viết về scope
và lexical scope
:
Sau khi tìm hiểu về 2 khái niệm này thì chúng ta có thể dễ dàng hơn trong việc hiểu closure
là gì rồi ^^.
Closure trong JavaScript là gì?
Như chúng ta đã biết, lexical scope
cho phép một biến được khai báo bên ngoài một hàm, có thể truy xuất được khi sử dụng biến này bên trong một hàm khác.
Chúng ta cùng xem lại ví dụ trong bài viết trước:
function hamOBenNgoai() {
// scope
let text = 'outside';
function hamOBenTrong() {
// scope
console.log(text); // outside
}
hamOBenTrong(); // gọi hàm
}
hamOBenNgoai();
Bên trong scope hamOBenTrong()
, chúng ta có thể truy xuất biến text
được khai báo từ hamOBenNgoai()
, vì chúng ta đã truy xuất text
trong lexical scope
của nó. Và chúng ta cũng thấy hamOBenTrong()
được gọi bên trong lexical scope
của hàm này.
Bây giờ chúng ta sẽ thay đổi đoạn code trên, gọi hamOBenTrong()
bên ngoài lexical scope
(scope của hamOBenNgoai()
) của nó xem chuyện gì sẽ xảy ra nhé!
Thay vì gọi hàm, mình sẽ return về hamOBenTrong()
như sau:
function hamOBenNgoai() {
// scope
let text = 'outside';
function hamOBenTrong() {
// scope
console.log(text); // outside
}
return hamOBenTrong; // return thay vì gọi hàm
}
function run(){
const hamBenNgoai = hamOBenNgoai();
hamBenNgoai();
}
run();
Bạn đoán thử xem, đoạn code trên có chạy thành công không? Liệu hamOBenTrong()
có thể truy xuất text
? Câu trả lời là có.
Trong JavaScript, các local variable tồn tại trong quá trình function thực thi. Nhưng khi function thực thi xong, các biến này sẽ không còn tồn tại và chúng sẽ không thể truy xuất được nữa.
Tuy nhiên, các bạn có thể thấy sau khi hamOBenNgoai()
thực thi, inner function
(function được return) là hamOBenTrong()
vẫn có thể truy xuất biến text
của hamOBenNgoai()
😮, tại sao lại như thế? Cái mà bạn đang thắc mắc chính là một đặc điểm của closure
.
Khi hamOBenNgoai()
thực thi, hamOBenTrong
sẽ ghi nhớ các biến từ bên ngoài(Biến không được khởi tạo bên trong hàm) mà nó sử dụng.
Vì lý do đó, bất kể bạn gọi hamOBenTrong
ở đâu thì nó có thể truy xuất đến các biến này. Nói cách khác hamOBenTrong
sẽ ghi nhớ các biến được sử dụng trong lexical scope
của nó.
Ở ví dụ trên, ta có thể gọi hamOBenTrong()
là một closure
.
Vậy function là một closure
khi function này có thể sử dụng các biến từ lexical scope
của nó, ngay cả khi function này thực thi bên ngoài lexical scope
của nó. Hay các inner function
có thể sử dụng các biến từ parent scope
của nó, ngay cả khi parent function
đã thực thi xong thì function đó cũng là một closure
.
Ví dụ về closure
Ta cùng xem một vài ví dụ về closure trong Javascript nhé!
Cùng xem một ví dụ về setTimeout():
const text = 'homiedev.com';
setTimeout(function showText() {
console.log(text); // homiedev.com
}, 1000);
Ở ví dụ trên showText()
là một closure
vì trong function có sử dụng biến text
từ lexical scope
của nó.
function increment() {
let sum = 0;
return function plusByOne() {
return sum++;
};
}
const showValue = increment();
showValue(); // 0
showValue(); // 1
showValue(); // 2
Trong ví dụ này plusByOne()
là một closure
vì nó sử dụng biến bên ngoài là sum
.
function plusByOne()
được gán cho biến showValue
khi thực thi xong increment()
. Câu hỏi là tại sao khi thực thi showValue()
, ta nhận được giá trị khác nhau mà không phải là giá trị 0
.
Câu trả lời là closure
sẽ lưu trữ biến sum
vào bộ nhớ theo kiểu reference. Tức là khi bạn thay đổi giá trị sum
thì nó sẽ update lại giá trị sum
.
Lần tiếp theo gọi showValue()
nó sẽ lấy sum
xem giá trị bao nhiêu và tiếp tục thực hiện sum++
. Đây là lí do tại sao ta nhận được kết quả như trên.
Bạn có thể dùng dir để hiểu kết quả trên hơn.
console.dir(showValue);
Ta sẽ có các properties của showValue như hình dưới đây:
Ở hình trên ta thấy closure
đã ghi nhớ biến được sử dụng là sum
với giá trị khởi tạo là 0 trong một object. Khá hay đúng không 😁. Hy vọng bài viết này sẽ giúp bạn hiểu closure
trong JavaScript hơn ^^.
Kết luận
Như vậy là chúng ta đã tìm hiểu về closure
trong JavaScript. Đây là một khái niệm phải nói là khá khó cho các bạn nào mới tiếp cận JavaScript.
Mình hy vọng bài viết sẽ giúp ích cho các bạn. Hẹn gặp các bạn trong các bài viết tiếp theo ^^.