Bindings, temporal dead zone, closures và modules
Closure giữ access tới lexical environment, không snapshot value; module graph và mutable shared state cần lifecycle rõ.
1. Declarations và TDZ
let/const block-scoped và ở temporal dead zone trước initialization; var function/global-scoped với hoisting behavior khác. Function declaration rules phụ thuộc context/strict mode. TDZ là runtime error khi đọc binding trước initialization, không phải undefined value.
{
// console.log(value); // ReferenceError (TDZ)
let value = 1;
}
2. Closure và lifetime
Function mang lexical environment nên callback/factory có thể truy cập binding ngoài scope. Closure hữu ích cho encapsulation nhưng giữ object graph lớn và event listener có thể gây retention. Loop let tạo per-iteration binding; var dùng chung binding.
| Pattern | Behavior | Risk/control |
|---|---|---|
| Factory closure | Private state qua returned functions | Expose method tối thiểu; dispose resources |
| Async callback | Giữ context tới callback chạy | Abort/unsubscribe khi component/request chết |
| Loop callback | let per iteration; var shared | Test captured value và timing |
3. ES modules
ES modules có static imports, live bindings và singleton per resolved module/realm. Top-level await tạo async evaluation; circular dependencies có thể đọc binding chưa initialized. Host/bundler quyết định resolution, extension, condition và loading policy.
- Import binding không phải snapshot copy; exporter thay đổi thì reader có thể thấy giá trị mới.
- Cycle cần thiết kế initialization order hoặc tách module để tránh TDZ/circular side effect.
- Tree shaking cần ESM/static graph và side-effect metadata đúng.
4. Production lifecycle
Không dùng module singleton làm request/user state trong server; dependency injection và explicit lifecycle giúp test/isolation. Nếu singleton giữ cache/listener/timer, phải có ownership, eviction và shutdown hook. Module state immutable/config-only thường dễ reasoning hơn.