Lesson 1.2.2 — Global Scope (Phạm vi Toàn cục)
0. Metadata
| Trường | Giá trị |
|---|---|
| Stage | 1 — JavaScript Execution Model |
| Module | 1.2 — Scope & Lexical Environment |
| Lesson | 1.2.2 |
| Competency | C02 — JavaScript Runtime |
| Depth | L2–L3 (Explain → Use) |
| Prerequisites | Lesson 1.2.1 — Scope là gì?, Execution Context (Module 1.1) |
| Cognitive Load | Trung bình |
1. Why This Exists (Vì sao cần học)
Bạn thêm một đoạn script analytics vào trang web. Đoạn script đó khai báo:
var data = { tracking: true };Ứng dụng của bạn cũng có:
const data = { user: "alice" };Production Risk
Trong một số trường hợp, biến data của bạn bị ghi đè hoặc gây lỗi. Nếu bạn không hiểu global scope, bạn sẽ fix bằng cách đổi tên biến (trial-and-error) thay vì hiểu root cause: cả hai đoạn code đang chiến tranh trên cùng một global scope.
Global scope trả lời câu hỏi:
"Những biến nào tồn tại ở tầng trệt, và ai có thể nhìn thấy chúng?"
Nếu không nắm chắc, bạn sẽ không thể:
- Dự đoán được biến nào đang "lộ" ra ngoài.
- Tránh global pollution trong codebase lớn.
- Debug được lỗi
window is not definedkhi chuyển từ Browser sang Node.js.
2. Prerequisites (Yêu cầu đầu vào)
Bạn cần đã biết:
- Scope là "vùng nhìn thấy" của biến (Lesson 1.2.1).
- Execution Context là môi trường code đang chạy (Module 1.1).
const,let,varkhai báo biến cơ bản.
3. Learning Objectives (Mục tiêu học tập)
Sau lesson này, bạn có thể:
- Giải thích global scope là "scope ngoài cùng" mà mọi code đều nhìn thấy được từ trong ra.
- Phân biệt global scope và global object (
window/globalThis) ở mức conceptual. - Nhận diện global pollution và implicit global trong code.
- Dự đoán kết quả của global lookup trong Browser và Node.js.
- Refactor code để giảm dependency vào global scope.
4. Mental Model (Mô hình tư duy)
Global Scope = Tầng Trệt Công Cộng
Hãy hình dung codebase như một tòa nhà:
Tầng Trệt (Global Scope)
├── var/let/const khai báo ở top-level
└── Mọi phòng (function/block) đều nhìn thấy ra tầng trệt- Từ bất kỳ phòng nào, bạn có thể nhìn thấy biến ở tầng trệt.
- Nhưng vì là công cộng, bất kỳ ai cũng có thể đặt đồ vào, lấy đồ ra, hoặc vô tình đè lên đồ của bạn.
Sai lầm phổ biến
"Global scope chỉ tồn tại trong Browser."
Sai. Global scope tồn tại trong mọi JavaScript runtime (Browser, Node.js, Bun, v.v.). Nhưng global object khác nhau: Browser có window, Node.js có global, cả hai đều có globalThis.
5. Core Concepts (Các khái niệm cốt lõi)
| Loại | Nội dung |
|---|---|
| Essential (Bắt buộc) | Global scope, Global bindings, Global lookup, Global pollution |
| Supporting (Hỗ trợ) | Global object (window/globalThis), Browser vs Node.js conceptual difference |
| Awareness (Biết tồn tại) | var tạo property trên global object; let/const thì không (nhưng vẫn thuộc global scope) |
| Out of Scope (Không học ở đây) | Hoisting (Module 1.3), Module scope (Stage 7) |
6. Worked Example (Ví dụ phân tích từng bước)
Input:
const apiUrl = "https://api.example.com";
function fetchUser() {
return fetch(apiUrl);
}
fetchUser();Step 1 — Observation: apiUrl được khai báo ở top-level. fetchUser cần resolve apiUrl và fetch.
Step 2 — Classification: Đây là global lookup. apiUrl không có trong scope của fetchUser, nên engine nhìn ra ngoài đến global scope.
Step 3 — Reasoning:
| Identifier | Local fetchUser | Global Scope | Kết quả |
|---|---|---|---|
apiUrl | ❌ Không có | ✅ Tìm thấy | "https://api.example.com" |
fetch | ❌ Không có | ✅ Tìm thấy (Browser API) | Hàm fetch |
Step 4 — Conclusion: Code chạy thành công vì global scope cung cấp cả apiUrl (binding do developer tạo) và fetch (binding do Browser cung cấp). Nhưng apiUrl nằm ở global scope nghĩa là bất kỳ đoạn code nào khác cũng có thể đọc/ghi nó.
7. Prediction Exercise (Bài tập dự đoán)
Đừng chạy code. Đoán output hoặc lỗi, sau đó giải thích tại sao.
P1 — Global Object Binding
var x = 1;
let y = 2;
console.log(window.x);
console.log(window.y);
console.log(globalThis.x);
console.log(globalThis.y);[Đáp án & Giải thích]
- Bạn nghĩ:
window.x=1,window.y=undefined,globalThis.x=1,globalThis.y=undefined. - Giải thích:
vartạo property trên global object (window/globalThis).let/constthuộc global scope nhưng không tạo property trên global object. Đây là điểm khác biệt quan trọng giữavarvàlet/constở top-level.
P2 — Implicit Global (Non-strict)
function setName() {
name = "Alice";
}
setName();
console.log(name);
console.log(window.name);[Đáp án & Giải thích]
- Bạn nghĩ: In ra
"Alice"hai lần. Vìnamekhông được khai báo bằngconst/let/var, JavaScript ở non-strict mode tự động tạo nó thành property của global object. - Giải thích: Đây là implicit global — một trong những nguồn gốc của global pollution. Ở strict mode (
"use strict"), dòngname = "Alice"sẽ throwReferenceErrorngay lập tức, giúp phát hiện lỗi sớm.
P3 — Browser vs Node.js
// File: app.js
console.log(typeof window);
console.log(typeof globalThis);[Đáp án & Giải thích]
- Bạn nghĩ: Trong Browser:
"object","object". Trong Node.js:"undefined","object". - Giải thích: Node.js không có
window, nhưng cóglobalThistrỏ đếnglobal. Đây là lý do code dùngwindowsẽ crash khi chạy trong Node.js/SSR. DùngglobalThisnếu cần truy cập global object một cách cross-environment.
8. Implementation Lab (Bài lab thực hành)
Lab: Audit Global Dependency
Cho đoạn code sau:
const config = { theme: "dark" };
let user = null;
function render() {
console.log(config.theme);
}
function login(username) {
user = username;
render();
}
login("alice");Yêu cầu:
- Liệt kê tất cả identifier đang được resolve từ global scope bên trong
render()vàlogin(). - Refactor
render()vàlogin()để nhận dependency qua tham số thay vì đọc global.
[Đáp án tham khảo]
- Bạn nghĩ: Trong
render():configvàconsole. Tronglogin():user,render,config(quarender),console. - Giải thích:
config,user,renderđều là global bindings.consolelà global API do host cung cấp. - Refactor:
function render(config) {
console.log(config.theme);
}
function login(username, config, renderFn) {
const user = username;
renderFn(config);
return user;
}
const config = { theme: "dark" };
login("alice", config, render);- Lưu ý:
consolevẫn là global lookup (điều này bình thường), nhưngconfigvàrenderkhông còn phụ thuộc global scope.
9. Edge Cases (Các trường hợp ngoại lệ)
EC-1: var vs let/const trên Global Object
var a = 1;
let b = 2;
delete window.a; // false (configurable: false)
delete window.b; // true (vì b không tồn tại trên window)What fails? Nếu nghĩ let cũng tạo property trên window, bạn sẽ bối rối khi window.b là undefined.
Why? var tạo [[Configurable]]: false property trên global object. let/const tạo binding trong global lexical environment nhưng không tạo property trên global object. Vì vậy delete window.b trả về true đơn giản vì property b không tồn tại trên window — delete hoàn toàn không tác động được đến let/const binding.
EC-2: Implicit Global trong Nested Function
function outer() {
function inner() {
count = 0;
}
inner();
}
outer();
console.log(count);What fails? count không khai báo, trở thành global. Nếu bên ngoài đã có count, nó bị ghi đè.
Fix: "use strict" hoặc khai báo rõ ràng bằng const/let.
10. Debug Lab (Bài lab gỡ lỗi)
Symptom (Triệu chứng): Sau khi thêm thư viện analytics, biến user trong ứng dụng đột nhiên trở thành undefined hoặc sai kiểu dữ liệu.
Reproduction (Tái hiện lỗi):
<script src="analytics.js"></script>
<script src="app.js"></script>// analytics.js
var user = { tracked: true };
// app.js
const user = { name: "alice" };Evidence (Bằng chứng): app.js throw SyntaxError: Identifier 'user' has already been declared (nếu dùng const/let) hoặc user bị ghi đè (nếu dùng var).
Hypothesis (Giả thuyết): Cả hai script cùng khai báo user ở global scope, gây xung đột tên.
Verification (Xác minh): Kiểm tra window.user trong DevTools trước và sau khi load từng script.
Root Cause (Nguyên nhân gốc rễ): Global scope là shared namespace cho tất cả script trong cùng một execution context. Không có boundary tự nhiên giữa các file script khi chúng cùng chạy trong cùng một global context.
Fix (Sửa):
// analytics.js
(function () {
var user = { tracked: true };
// logic...
})();Hoặc đưa app vào một hàm wrapper để tạo local scope.
Prevention (Phòng ngừa): Kiểm tra global bindings trước khi thêm script mới. Trong production, ưu tiên module system (Stage 7) hoặc bundler để tự động scope isolation.
11. Design Exercise (Bài tập thiết kế)
Không áp dụng ở depth L2–L3. Design Exercise sẽ xuất hiện từ các lesson L5–L6 trở đi.
12. Production Scenario (Tình huống thực tế)
Context: Bạn đang viết một thư viện UI nhỏ để embed trên website của khách hàng. Thư viện cần lưu trữ trạng thái cấu hình.
Symptom: Khi khách hàng embed thư viện của bạn cùng với một thư viện khác, cấu hình của bạn bị ghi đè.
Decision: Bạn nhận ra thư viện của mình đang khai báo:
var config = { apiKey: "secret" };Ở global scope. Điều này vi phạm nguyên tắc least exposure.
Fix: Bọc toàn bộ thư viện trong một IIFE (Immediately Invoked Function Expression) để tạo function scope riêng, chỉ expose API cần thiết ra global (nếu thực sự cần).
(function () {
const config = { apiKey: "secret" };
// ... library logic ...
window.MyLibrary = {
init: function () {
/* ... */
},
};
})();💡 Trade-off:
window.MyLibraryvẫn là một global property, nhưng bạn đã giảm từ N biến global xuống còn 1. Đây là cách tiếp cận "least exposure" trong thời kỳ pre-ESM.
Quyết định kỹ thuật
Nếu bạn kiểm soát được build pipeline, module scope (ESM) là giải pháp triệt để hơn. Tuy nhiên, ESM thuộc Stage 7. Ở Stage 1, function scope là công cụ isolation đáng tin cậy nhất.
13. AI-Assisted Exercise (Bài tập với AI)
Level A — Ask
- Tự trả lời trước: Viết ra giấy: "Global scope khác global object như thế nào?
letkhai báo ở top-level có tạo property trênwindowkhông?" - Hỏi AI:
What is the difference between global scope and global object in JavaScript? Does let create a property on window? - So sánh: AI có nói "all global variables are properties of window" không? Nếu có, đó là điểm mù.
- Verify: Đối chiếu với MDN — Global object.
Gợi ý
Để ý xem AI có nhầm:
- "Global scope = global object" (sai: scope là cấu trúc visibility, object là runtime entity).
- "
letvàvarđều tạo property trênwindow" (sai: chỉvarvà function declaration).
Đáp án tham khảo
- Bạn nghĩ: Global scope là vùng nhìn thấy ở top-level. Global object (
window/globalThis) là object runtime chứa các API và một số binding.let/constở global scope không tạo property trên global object, nhưng vẫn có thể nhìn thấy từ mọi nơi. - AI trả lời: Thường nói đúng rằng global object chứa built-in API, nhưng hay gộp chung "global variables live on the global object" — bỏ qua sự khác biệt giữa
varvàlet/const. - So sánh: AI thiếu precision ở điểm: global scope có hai loại binding — object property binding (
var) và lexical binding (let/const). Điểm này quan trọng để debug lỗiwindow.myVarvsmyVar. - Điểm AI nói sai hoặc quá mơ hồ: AI thường nói "global variables are accessible via the global object" nhưng không phân biệt
varvslet. Điều này gây hiểu nhầm rằngwindow.xluôn truy cập được mọi biến global. - Kết luận: Nếu bạn chỉ ra được sự phân biệt giữa global lexical environment và global object property, bạn đã hiểu sâu hơn AI. Điều này sẽ giúp bạn debug SSR và global pollution ở các Stage sau.
14. Teach Back (Dạy lại)
Yêu cầu: Giải thích cho một junior dev trong 2 phút:
"Tại sao
letkhai báo ở top-level không xuất hiện trênwindow, nhưngvarthì có? Điều này ảnh hưởng gì đến việc debug?"
Dùng đúng terminology: global scope, global object, lexical binding, object property binding.
Mô phỏng
- Bạn nói: Khi bạn viết
var x = 1ở top-level, JavaScript tạo một propertyxtrên global object (windowtrong Browser). Nhưng khi viếtlet y = 2, JavaScript tạo một binding trong global lexical environment — vẫn thuộc global scope, vẫn nhìn thấy từ mọi nơi, nhưng không phải là property củawindow. Điều này ảnh hưởng debug vì nếu bạn quen mở DevTools gõwindow.xđể kiểm tra biến global, bạn sẽ bối rối khiwindow.ytrả vềundefinedmặc dùyđang tồn tại. Hiểu điều này giúp bạn không bị lừa khi audit global state.
💡 Hình dung
windownhư một tủ đồ công cộng:varbỏ đồ vào tủ,lettreo đồ trên tường cùng phòng — vẫn trong phòng, nhưng không ở trong tủ.
Gợi ý đánh giá bản thân
- Đồng nghiệp có hiểu tại sao
window.ylàundefinedkhông? - Bạn có thể dự đoán được hành vi của
constkhông? - Nếu họ hỏi "vậy làm sao liệt kê tất cả biến global", bạn trả lời được không?
15. Assessment (Đánh giá)
| Objective (Mục tiêu) | Hình thức đánh giá | Nội dung |
|---|---|---|
| Giải thích global scope là scope ngoài cùng | Explain (Giải thích) | Dùng lời riêng mô tả global scope và tại sao mọi function đều nhìn thấy ra ngoài |
| Phân biệt global scope và global object | Prediction (Dự đoán) + AI Exercise | Dự đoán window.x vs window.y với var và let |
| Nhận diện global pollution | Debug Lab | Tìm root cause của biến bị ghi đè bởi script thứ ba |
| Dự đoán global lookup cross-environment | Prediction (P3) | Đoán typeof window trong Browser vs Node.js |
| Refactor giảm global dependency | Implementation Lab | Chuyển global bindings thành tham số function |
16. Exit Criteria (Tiêu chí qua bài)
- [ ] Có thể giải thích global scope là "tầng trệt" mà mọi code đều nhìn thấy từ trong ra.
- [ ] Có thể phân biệt
vartạo property trên global object, cònlet/constthì không. - [ ] Dự đoán đúng 3/3 scenarios trong Prediction Exercise.
- [ ] Có thể liệt kê ít nhất 3 global bindings trong một đoạn code cho trước.
- [ ] Refactor được code để giảm global dependency bằng cách truyền tham số.
17. Spiral Connection (Liên kết xoắn ốc)
Previous (Trước): Scope là gì (Lesson 1.2.1) — hiểu scope là vùng nhìn thấy.
Current (Hiện tại): Global Scope — hiểu scope ngoài cùng, global lookup, và global pollution.
Next (Tiếp theo):
- Lesson 1.2.3 — Function Scope (scope bên trong hàm)
- Lesson 1.2.4 — Block Scope (
{ },let,const)- Lesson 1.2.5 — Nested Scope & Shadowing
- Module 1.4 — Closure (global scope là outer environment của mọi closure)
- Stage 7 — Modules (ESM tạo module scope, giải quyết triệt để global pollution)
- Stage 9 — Production Frontend (third-party script isolation, CSP)