Closure JavaScript Explained: Eliminates All Abstractions

Closure JavaScript Giải Thích Dễ Hiểu: Xóa Tan Mọi Trừu Tượng

Have you ever messed around with a variable being unexpectedly changed in a callback function, or struggled to create "private" variables in JavaScript? I used to scratch my head like that in the first days of coding. It seems like you have to use a complicated library, but it turns out the answer lies right in one of the most core concepts of this language. If you're looking for an article about Closure JavaScript that's easy to understand, you've come to the right place. That is Closure - something that sounds abstract at first but is actually super easy to understand. If you're new, a quick glance at Learn JavaScript Basics for Beginners 2026 will give you the perfect knowledge buffer before we dive into this article.

Breaking the mystery: What the heck is a Closure?

A Closure is simply a function that remembers the environment in which it was created, including local variables, even if the function containing it has long finished executing.

When it comes to the concept of Closure in Javascript, many people often think of a bunch of dry theories in academic documents. At Pham Hai, after many years of directly guiding and training freshers, I realize that the biggest barrier is the way we approach problems. Don't think of it as a super feature that you have to actively "turn on" or install. It's always there, silently working in every line of code you write.

Easiest definition to swallow: Not "creation", but "instinct"

Closure is not something you write code to create, it is the natural ability of functions in JavaScript to store and access variables of parent functions.

When learning how Closure works, you just need to remember one golden rule: Functions in JavaScript are "heavy". It always remembers the origin where it was defined. An inner function (inner function) will always have access to the variables of the outer function (outer function) that contains it.

The amazing thing is, even though the outer function has finished running, returned its results, and been removed from the call stack, its variables are not destroyed. They are safely "closed" so that the inner jaw can continue to be used later. That is the natural instinct of this language.

Core foundation: Everything starts with Lexical Scope

Lexical Scope is a rule that determines the scope of variables based on their physical position in the source code when you type the code.

To truly "absorb" Closure, we must agree on Lexical Scope, also known as lexical scope. The JavaScript compiler looks at where you type the code to decide which variables belong in which scope. This concept is closely related to lexical environment (Lexical Environment).

Biến được khai báo ở đâu thì nó sẽ "thuộc về" khối code đó, khác biệt hoàn toàn với việc hàm được gọi (invoke) ở đâu. Khi làm việc với các dự án hiện đại, việc áp dụng ES6 JavaScript tính năng mới cần biết cùng các từ khóa block-scoped như letconst càng làm rõ ràng hơn ranh giới của các phạm vi này, giúp bạn kiểm soát code tốt hơn.

How it works: Why does the inner function 'remember' the variable of the parent function?

The internal function remembers variables thanks to the Scope Chain, allowing it to search backwards from its scope to the global scope.

The secret behind this divine memorization lies in the Scope Chain and the execution environment. When a function needs to find the value of a variable, it acts in the following order of priority:

  1. Look in its own local variable first.
  2. If there isn't one, it will "look" at the parent function that wraps it.
  3. Just like that, extend the scope chain all the way to global variable (Global Scope).

Thanks to Scope Chain, the Closure and Scope mechanisms work together smoothly. It helps the inner function retain a path (reference) to the necessary variables in the parent function, preventing them from "evaporating" from memory after the parent function ends.

Not just a theory: What to use Closure for in practice?

Closure is widely used to secure data, create flexible functions with Factory/Currying, optimize performance and manage event listeners effectively.

Having grasped the theory, the next question is definitely: Why use Closure and what is the application of Closure in Javascript in practice? Below are real-life "hacks" that the team at Pham Hai regularly applies to solve difficult problems.

Application #1: Create a data security 'safe' with Module Pattern

The Pattern module uses Closure to create private variables, which help encapsulate data and prevent unauthorized intervention by outside code.

Đây là một trong những ứng dụng kinh điển và cũng là một ví dụ Closure JavaScript tuyệt vời nhất để thực hiện tính đóng góibảo mật dữ liệu. Trong JavaScript, trước khi các class private fields (#) ra đời và phổ biến, chúng ta hoàn toàn dựa vào Closure để giả lập biến private.

Hãy tưởng tượng bạn viết một hàm quản lý tài khoản ngân hàng. Bạn khai báo một biến balance (số dư) bên trong hàm đó, và chỉ return ra hai hàm con là deposit (nạp) và withdraw (rút). Code bên ngoài hệ thống chỉ có thể gọi lệnh nạp/rút, tuyệt đối không thể tự ý gán bank.balance = 1000000000 được. Dữ liệu của bạn đã được "cất vào két sắt" an toàn nhờ Module Pattern.

App #2: 'Function Factory' with Function Factory and Currying Function

Function Factory and Currying Function take advantage of Closure to create new functions with pre-set parameters, helping to maximize code reuse.

Instead of writing many functions with similar logic over and over again, we can create a Function Factory. This is a "mother" function that produces "child" functions that have a few pre-configured parameters.

Similarly, the Currying Function technique helps transform a function that takes many parameters into a series of functions, each of which only takes a single parameter. For example, you create a function that generates HTML tags with a predefined class. When you need to further manipulate these tags on the interface, combining with DOM JavaScript to manipulate HTML elements will help you build UI extremely flexibly and code much more neatly.

Application #3: Handling Event Listeners and Callback functions is no longer messy

Closure ensures that callback functions in the Event Listener always access the correct value of the variable at the time the event is initialized and attached to the element.

When you have to attach an Event Listener to a long list of buttons, the callback function needs to know exactly which button has just been interacted with. Closure now acts as an anchor, helping to "lock" the value of the variable scope for each individual listener.

If you do not understand and apply Closure, it is likely that every button you click will log the same final value of the initialization loop. This is an extremely common logic error that newcomers often encounter.

Application #4: Optimize Performance with Memoization

Memoization uses Closure to store (cache) the results of heavy calculations, helping to optimize performance by avoiding the need to recalculate multiple times.

In web development, performance optimization is always a matter of survival. With the Memoization technique, Closure acts as a local temporary memory (cache). When a function with complex calculation logic finishes running, Closure will silently save the result.

Next time, if you call that function again with the same set of input parameters, it will not recalculate from the beginning but immediately pull the result from the cache and return it. This application shines especially when you have to deal with huge arrays of data, where overusing JavaScript Array methods map filter reduce without a cache mechanism can crash the user's browser.

Hidden corners you may not know (and employers often ask about)

Các vấn đề thường gặp với Closure bao gồm lỗi vòng lặp for với từ khóa var, nguy cơ rò rỉ bộ nhớ (Memory Leaks) và sự phức tạp trong luồng xử lý bất đồng bộ.

The topic of Closure in JavaScript interviews is always a "specialty dish" that seniors use to test candidates' thinking. Here are some practical pitfalls that you need to understand so you don't get confused.

Classic pitfall: Closures and 'for' loops

Dùng từ khóa var trong vòng lặp chứa hàm bất đồng bộ sẽ khiến Closure ghi nhớ tham chiếu cuối cùng của biến, gây ra lỗi logic hiển thị sai số.

Đây là ví dụ "huyền thoại" sống mãi với thời gian. Bạn viết một vòng lặp for sử dụng từ khóa var, bên trong gọi một hàm setTimeout để in ra giá trị của biến đếm i. Kết quả nhận được không phải là 0, 1, 2… mà toàn là số cuối cùng của vòng lặp?

Nguyên nhân là do var có phạm vi function scope (hoặc global), không phải block scope. Closure trong các hàm setTimeout đều trỏ chung về một vùng nhớ của biến i. Cách khắc phục triệt để và hiện đại nhất là thay var bằng let để tạo ra một môi trường từ vựng mới cho mỗi vòng lặp, hoặc sử dụng IIFE (Immediately Invoked Function Expression) nếu bạn phải maintain code cũ.

Problem Reason How to fix it
Print out the entire final number var không có block scope, Closure giữ chung 1 tham chiếu Dùng let trong vòng lặp for
Old code does not use ES6 Need to create separate scope for each iteration Wrap the logic with the IIFE function

The dark side of power: Closure and the risk of memory leaks (Memory Leaks)

Because Closure keeps a strict reference to the parent function's variables, it can unintentionally prevent the Garbage Collection from releasing memory, leading to Memory Leaks.

JavaScript's Garbage Collection works based on a mark-and-sweep mechanism, checking whether a memory area is still referenced or not. If you abuse Closure without control, you can accidentally keep objects or huge arrays of data in memory that the application will never use again.

Đây là nguyên nhân hàng đầu gây ra hiện tượng Memory Leaks (rò rỉ bộ nhớ), làm web chạy chậm dần đều. Lời khuyên xương máu từ mình là hãy chủ động gán null cho các biến hoặc hàm chứa tham chiếu lớn sau khi đã sử dụng xong để "giải thoát" cho bộ nhớ.

Closely related to Promises and asynchronous processing

In asynchronous processing, Promises and callback functions use Closure to maintain state and access local variables when data is returned in the future.

Khi bạn thực hiện gọi API lấy dữ liệu, bạn thường dùng Promise hoặc cú pháp async/await. Trong lúc hệ thống chờ mạng phản hồi, hàm bên ngoài thực chất đã chạy xong từ lâu. Thế nhưng, đoạn code nằm bên trong .then() hoặc phía sau await vẫn truy cập mượt mà vào các biến được khai báo ở hàm ngoài.

That's when you're enjoying the power of Closure in asynchronous processing without even realizing it. If you're still a little confused about the flow of events, reading this breakdown of Async Await Promise Easy to Understand JavaScript will help you put the pieces of the puzzle together perfectly.

In short, never view Closure as a barrier or an over-the-top feature. Think of it simply as a natural consequence of the Lexical Scope mechanism. Once you actually "live it" and understand how data flow works through real-life examples, you will find it not only easy to understand but also an extremely sharp tool. Mastering Closure is the turning point that helps you transform from a rote coder to a real software engineer, writing cleaner, more secure code and optimizing better performance.

Have you ever encountered any "crying bad" bugs related to incorrect variable values ​​in callbacks where Closure is the savior? Or do you have an interview question on this topic that sticks out in your mind? Please leave a comment sharing your story below for the community to discuss and learn!

Lưu ý: Thông tin trong bài viết này chỉ mang tính chất tham khảo. Để có lời khuyên tốt nhất, vui lòng liên hệ trực tiếp với chúng tôi để được tư vấn cụ thể dựa trên nhu cầu thực tế của bạn.

Categories: JavaScript Lập Trình Web

mrhai

Để lại bình luận