Skip to content

Lesson 1.2.2 — Global Scope (Phạm vi Toàn cục) ​

Bài 1.2.2 — Global Scope
Global Bindings, Top-level Code & Scope Boundaries • 18 phút
0:00 / 0:00

0. Metadata ​

TrườngGiá trị
Stage1 — JavaScript Execution Model
Module1.2 — Scope & Lexical Environment
Lesson1.2.2
CompetencyC02 — JavaScript Runtime
DepthL2–L3 (Explain → Use)
PrerequisitesLesson 1.2.1 — Scope là gì?, Execution Context (Module 1.1)
Cognitive LoadTrung 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:

js
var data = { tracking: true };

Ứng dụng của bạn cũng có:

js
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 defined khi 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, var khai 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ể:

  1. 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.
  2. Phân biệt global scope và global object (window/globalThis) ở mức conceptual.
  3. Nhận diện global pollution và implicit global trong code.
  4. Dự đoán kết quả của global lookup trong Browser và Node.js.
  5. 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à:

text
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ạiNộ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:

js
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:

IdentifierLocal fetchUserGlobal ScopeKế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 ​

js
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: var tạo property trên global object (window/globalThis). let/const thuộ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ữa var và let/const ở top-level.

P2 — Implicit Global (Non-strict) ​

js
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ì name không được khai báo bằng const/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òng name = "Alice" sẽ throw ReferenceError ngay lập tức, giúp phát hiện lỗi sớm.

P3 — Browser vs Node.js ​

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ó globalThis trỏ đến global. Đây là lý do code dùng window sẽ crash khi chạy trong Node.js/SSR. Dùng globalThis nế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:

js
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:

  1. Liệt kê tất cả identifier đang được resolve từ global scope bên trong render() và login().
  2. Refactor render() và login() để nhận dependency qua tham số thay vì đọc global.
[Đáp án tham khảo]
  • Bạn nghĩ: Trong render(): config và console. Trong login(): user, render, config (qua render), console.
  • Giải thích: config, user, render đều là global bindings. console là global API do host cung cấp.
  • Refactor:
js
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 ý: console vẫn là global lookup (điều này bình thường), nhưng config và render khô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 ​

js
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 ​

js
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):

html
<script src="analytics.js"></script>
<script src="app.js"></script>
js
// 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):

js
// 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:

js
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).

js
(function () {
  const config = { apiKey: "secret" };
  // ... library logic ...
  window.MyLibrary = {
    init: function () {
      /* ... */
    },
  };
})();

💡 Trade-off: window.MyLibrary vẫ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

  1. Tự trả lời trước: Viết ra giấy: "Global scope khác global object như thế nào? let khai báo ở top-level có tạo property trên window không?"
  2. Hỏi AI: What is the difference between global scope and global object in JavaScript? Does let create a property on window?
  3. So sánh: AI có nói "all global variables are properties of window" không? Nếu có, đó là điểm mù.
  4. 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).
  • "let và var đều tạo property trên window" (sai: chỉ var và 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 var và 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ỗi window.myVar vs myVar.
  • Đ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 var vs let. Điều này gây hiểu nhầm rằng window.x luô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 let khai báo ở top-level không xuất hiện trên window, nhưng var thì 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 property x trên global object (window trong Browser). Nhưng khi viết let 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ủa window. Đ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 khi window.y trả về undefined mặ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 window như một tủ đồ công cộng: var bỏ đồ vào tủ, let treo đồ 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.y là undefined không?
  • Bạn có thể dự đoán được hành vi của const khô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ùngExplain (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 objectPrediction (Dự đoán) + AI ExerciseDự đoán window.x vs window.y với var và let
Nhận diện global pollutionDebug LabTìm root cause của biến bị ghi đè bởi script thứ ba
Dự đoán global lookup cross-environmentPrediction (P3)Đoán typeof window trong Browser vs Node.js
Refactor giảm global dependencyImplementation LabChuyể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 var tạo property trên global object, còn let/const thì 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)
📴 Offline Mode — Content served from cache