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à scopelexical 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ề scopelexical 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

(

)

{

let

text

=

'outside'

;

function

hamOBenTrong

(

)

{

console

.

log

(

text

)

;

}

hamOBenTrong

(

)

;

}

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

(

)

{

let

text

=

'outside'

;

function

hamOBenTrong

(

)

{

console

.

log

(

text

)

;

}

return

hamOBenTrong

;

}

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à .

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

)

;

}

,

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

(

)

;

showValue

(

)

;

showValue

(

)

;

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:

example_closure

Ở 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 ^^.