Skip to content

Lesson 1.4.3 — Closure làm Private State ​

Bài 1.4.3 — Closure as Private State
Encapsulation, Persistent State & Controlled Access • 31 phút
0:00 / 0:00

0. Metadata (Thông tin bài học) ​

FieldValue
Stage1
Module1.4 — Closures
Lesson1.4.3 — Closure làm Private State
CompetencyC02 — JavaScript Runtime (C02.5 Closure)
Depth TargetL5 (Implement)
PrerequisitesClosure Formation (1.4.1), Closure và Lifetime (1.4.2), Lexical Environment (1.2.7), Variable Resolution (1.2.6)
Estimated Cognitive LoadHigh

Out of Scope (Ngoài phạm vi)

  • Factory patterns với nhiều loại instance/API (Lesson 1.4.4)
  • Constructor function, new, prototype và class (Stage 2)
  • JavaScript private class fields #field (Stage 2)
  • Module-level encapsulation và package boundaries (Stage 7/12)
  • Async state, callback lifecycle và race conditions (Stage 3)
  • React state / stale closure (Stage 8)
  • Garbage Collection internals và memory profiling (Stage 11)

1. Why This Exists (Vì sao cần học) ​

Bạn cần một counter có state:

js
let count = 0;

function increment() {
  count = count + 1;
}

Code chạy được, nhưng count đang nằm ở scope bên ngoài. Bất kỳ code nào có quyền truy cập binding đó đều có thể thay đổi nó:

js
count = 999;

Nếu requirement là:

Chỉ cho phép thay đổi state thông qua những operation đã định nghĩa.

thì việc expose trực tiếp count làm mất quyền kiểm soát state.

Closure cho phép ta thay đổi cách thiết kế:

js
const counter = createCounter();

counter.increment();
counter.increment();

console.log(counter.getValue()); // 2

Caller biết cách sử dụng state, nhưng không có identifier count để sửa trực tiếp.

Đây là bước chuyển quan trọng:

text
state
+
operations
+
controlled access

Nếu không hiểu private state bằng Closure, bạn sẽ dễ:

  • Đặt mutable state ở global scope.
  • Expose state rồi hy vọng caller "đừng sửa".
  • Nhầm private binding với immutable value.
  • Return một snapshot rồi tưởng đó là live state.
  • Không biết vì sao nhiều methods có thể cùng đọc/ghi một binding.
  • Thiết kế API theo data structure thay vì theo invariant cần bảo vệ.

2. Prerequisites (Yêu cầu đầu vào) ​

Trước khi học bài này, bạn phải:

  • [ ] Giải thích được Closure Formation.
  • [ ] Biết outer function return nhưng environment vẫn có thể reachable qua closure.
  • [ ] Phân biệt Execution Context với Lexical Environment.
  • [ ] Biết nhiều inner functions trong cùng invocation có thể cùng resolve một outer binding.
  • [ ] Phân biệt reassignment của binding với mutation của object.
  • [ ] Trace được Variable Resolution từ method/function ra outer environment.

Nếu thiếu, quay lại Lesson 1.4.1–1.4.2 trước.

3. Learning Objectives (Mục tiêu học tập) ​

Sau bài này, bạn có thể:

  1. Giải thích private state bằng environment + closure, không bằng câu "function nhớ biến".
  2. Implement createCounter() với increment(), decrement() và getValue().
  3. Chứng minh count không được expose trực tiếp qua public API.
  4. Dự đoán behavior khi nhiều methods cùng đọc/ghi một captured binding.
  5. Phân biệt private binding với immutable state và với object reference được expose.
  6. Debug API có state bên trong nhưng public property lại bị stale hoặc bypass invariant.
  7. Thiết kế một API nhỏ sao cho mutation đi qua operations được kiểm soát.

4. Mental Model (Mô hình tư duy) ​

Mental Model — Private State bằng Closure

Private state trong lesson này được tạo bằng cách:

  1. Đặt state trong lexical environment của outer function.
  2. Không return trực tiếp binding/state đó như một public property để caller sửa tùy ý.
  3. Return các functions/methods được phép đọc hoặc thay đổi state.
  4. Các functions đó giữ reference cần thiết đến outer environment.

Caller giữ API, còn state nằm trong environment được API closures truy cập.

Ví dụ:

js
function createCounter() {
  let count = 0;

  return {
    increment() {
      count = count + 1;
    },
    decrement() {
      count = count - 1;
    },
    getValue() {
      return count;
    },
  };
}

Mental model:

text
createCounter()
      ↓
Environment
└── count = 0
      ↑
      ├── increment()
      ├── decrement()
      └── getValue()

Caller
└── counter
    ├── increment
    ├── decrement
    └── getValue

Caller có reference đến ba methods, nhưng không có binding public:

js
counter.count;

trừ khi chính ta chủ động expose property đó.

Điểm quan trọng:

Closure không biến count thành "private keyword". Privacy ở đây đến từ lexical scope + API boundary: caller không có đường truy cập trực tiếp tới binding count, chỉ có các closures mà ta trả ra.

Private ≠ Immutable

count private không có nghĩa count không thể thay đổi.

Ngược lại, increment() và decrement() được thiết kế để thay đổi cùng một binding count.

Private ở đây nghĩa là quyền truy cập trực tiếp bị giới hạn bởi lexical scope, không phải state bị đóng băng.

5. Core Concepts (Các khái niệm cốt lõi) ​

Essential (Bắt buộc) ​

  • Private Binding: Binding nằm trong lexical scope mà caller không thể resolve trực tiếp từ bên ngoài.
  • Public API: Những functions/methods được return để caller tương tác với state.
  • Controlled Mutation: State chỉ được thay đổi qua operations mà API cho phép.
  • Shared Captured State: Nhiều methods trong cùng invocation có thể cùng đọc/ghi một binding.
  • Invariant: Điều kiện mà API phải luôn bảo vệ, ví dụ counter không được nhỏ hơn 0.

Supporting (Hỗ trợ) ​

  • Closure lifetime giúp private state tiếp tục tồn tại sau khi creator function return.
  • Getter method có thể cung cấp read access mà không expose binding để caller reassign trực tiếp.
  • Public API nhỏ giúp giảm số đường có thể mutate state.
  • Encapsulation trong lesson này là một design effect của lexical scope + closure.

Awareness (Biết tồn tại) ​

  • JavaScript có nhiều cách khác để tổ chức encapsulation/private state.
  • Class private fields, module boundaries và WeakMap có trade-off khác.
  • Object reference có thể làm rò rỉ khả năng mutation nếu API trả trực tiếp object mutable.

6. Worked Example (Ví dụ phân tích từng bước) ​

Worked Example — createCounter() với private state:

js
function createCounter() {
  let count = 0;

  return {
    increment() {
      count = count + 1;
    },
    decrement() {
      count = count - 1;
    },
    getValue() {
      return count;
    },
  };
}

const counter = createCounter();

counter.increment();
counter.increment();
counter.decrement();

console.log(counter.getValue()); // 1

Step 1 — createCounter() được gọi:

  • Tạo một invocation mới.
  • Lexical environment của invocation chứa binding count → 0.
  • Ba methods được tạo trong environment này.

Step 2 — Public API được return:

Caller nhận object:

text
increment
decrement
getValue

Nhưng object không có public property count.

Step 3 — counter.increment() được gọi:

  • Method cần resolve count.
  • Trong environment của chính method không có count.
  • Đi theo outer environment reference.
  • Tìm thấy binding count của createCounter.
  • Update count: 0 → 1.

Step 4 — Gọi tiếp increment() và decrement():

Cả ba methods đều dùng cùng một binding:

text
count
0 → 1 → 2 → 1

Không có ba bản copy count khác nhau.

Step 5 — getValue() đọc state:

js
counter.getValue(); // 1

getValue() không cần expose binding. Nó chỉ return giá trị hiện tại của binding đó.

Step 6 — Kết luận:

text
Caller
  ↓
Public API
  ↓
Closures
  ↓
Private binding: count

State được duy trì nhờ Closure Lifetime, còn quyền truy cập được kiểm soát bởi lexical scope và public API.

7. Prediction Exercise (Bài tập dự đoán) ​

Đừng chạy code. Dự đoán output và giải thích bằng binding + closure + API boundary.

Prediction 1

js
function createCounter() {
  let count = 0;

  return {
    increment() {
      count = count + 1;
    },
    getValue() {
      return count;
    },
  };
}

const counter = createCounter();

counter.increment();
counter.increment();

console.log(counter.getValue());
console.log(counter.count);

Prediction 2

js
function createCounter() {
  let count = 0;

  return {
    increment() {
      count = count + 1;
    },
    reset() {
      count = 0;
    },
    getValue() {
      return count;
    },
  };
}

const counter = createCounter();

counter.increment();
counter.increment();
counter.reset();

console.log(counter.getValue());

Prediction 3

js
function createState() {
  let state = { score: 0 };

  return {
    getState() {
      return state;
    },
  };
}

const store = createState();
const exposed = store.getState();

exposed.score = 100;

console.log(store.getState().score);
[Đáp án & Giải thích]
  • Prediction 1:

    • Output: 2 rồi undefined.
    • count nằm trong lexical environment của createCounter. Returned object không có property count, nên counter.count là undefined. getValue() vẫn đọc được binding vì method là closure.
  • Prediction 2:

    • Output: 0.
    • increment, reset, getValue cùng truy cập một binding count. reset() reassign binding đó về 0.
  • Prediction 3:

    • Output: 100.
    • Binding state là private, nhưng getState() return chính object reference đang nằm trong binding. Caller không reassign được binding state, nhưng vẫn có thể mutate object mà binding trỏ tới. Private binding không tự động làm object immutable.

Transfer Check — không cần biết pattern trước:

js
function createTemperature(initialValue) {
  let temperature = initialValue;

  return {
    increase() {
      temperature = temperature + 1;
    },
    read() {
      return temperature;
    },
  };
}

const room = createTemperature(25);

Hãy trả lời:

  1. Binding nào là private state?
  2. Caller có thể viết room.temperature = 100 để thay đổi binding private không?
  3. increase() và read() có cùng truy cập một binding không?
  4. Vì sao state vẫn tồn tại sau khi createTemperature() return?
Gợi ý trả lời
  1. temperature là private binding nằm trong environment của invocation createTemperature.
  2. Không. Gán room.temperature = 100 chỉ tạo/thay đổi property trên returned object; nó không phải binding temperature mà closures đang resolve.
  3. Có. Cả hai methods được tạo trong cùng invocation và cùng resolve temperature.
  4. Returned methods vẫn reachable và cần outer binding đó, nên environment cần thiết vẫn reachable theo mental model từ Lesson 1.4.2.

8. Implementation Lab (Bài lab thực hành) ​

Lab 1 — Guided: createCounter() ​

Bước 0 — Vẽ API boundary trước khi code.

text
Private
└── count

Public
├── increment()
├── decrement()
└── getValue()

Sau đó implement:

js
function createCounter() {
  // TODO
}

const counter = createCounter();

counter.increment();
counter.increment();
counter.decrement();

console.log(counter.getValue()); // 1
console.log(counter.count); // undefined

Constraints:

  • count bắt đầu từ 0.
  • Không dùng global variable.
  • Không lưu count bằng public property.
  • Chỉ increment() và decrement() được thay đổi count.
  • getValue() chỉ đọc state.
[Đáp án tham khảo]
js
function createCounter() {
  let count = 0;

  return {
    increment() {
      count = count + 1;
    },
    decrement() {
      count = count - 1;
    },
    getValue() {
      return count;
    },
  };
}
  • Giải thích: count chỉ tồn tại trong lexical environment của createCounter. Ba methods là closures cùng truy cập binding đó. Caller chỉ nhận API object, không nhận binding count.

Lab 2 — Partial Scaffold: Counter có invariant ​

Requirement mới:

Counter không được nhỏ hơn 0.

Hoàn thiện:

js
function createCounter() {
  let count = 0;

  return {
    increment() {
      // TODO
    },
    decrement() {
      // TODO: không cho count xuống dưới 0
    },
    getValue() {
      // TODO
    },
  };
}

Expected behavior:

js
const counter = createCounter();

counter.decrement();
console.log(counter.getValue()); // 0

counter.increment();
counter.increment();
counter.decrement();

console.log(counter.getValue()); // 1
[Đáp án tham khảo]
js
function createCounter() {
  let count = 0;

  return {
    increment() {
      count = count + 1;
    },
    decrement() {
      if (count > 0) {
        count = count - 1;
      }
    },
    getValue() {
      return count;
    },
  };
}
  • Giải thích: Vì caller không thể sửa count trực tiếp, API có thể bảo vệ invariant count >= 0 tại mọi mutation point.

Lab 3 — Independent: Private Score ​

Implement:

js
const score = createScore(10);

score.add(5);
score.subtract(3);

console.log(score.getValue()); // 12

Contract:

text
add(points)
subtract(points)
getValue()

Constraints:

  • Initial score được truyền vào creator function.
  • Score không được âm.
  • points phải lớn hơn 0; input không hợp lệ thì không thay đổi state.
  • State không được expose bằng property public.
  • Không dùng class.

Sau khi code, trả lời:

  1. Binding nào là state owner?
  2. Có bao nhiêu mutation points?
  3. Invariant được kiểm tra ở đâu?
  4. Vì sao getValue() an toàn hơn việc expose score để caller tự sửa?
[Một phương án tham khảo]
js
function createScore(initialScore) {
  let score = initialScore;

  return {
    add(points) {
      if (points <= 0) return false;

      score = score + points;
      return true;
    },
    subtract(points) {
      if (points <= 0 || points > score) return false;

      score = score - points;
      return true;
    },
    getValue() {
      return score;
    },
  };
}
  • score là private binding của creator invocation.
  • add và subtract là hai mutation points.
  • Validation nằm tại mutation boundary.
  • Caller không thể bypass rule bằng cách viết trực tiếp score = -100.

9. Edge Cases (Các trường hợp ngoại lệ) ​

Edge Case 1 — Public property chỉ là snapshot ​

js
function createCounter() {
  let count = 0;

  return {
    count,
    increment() {
      count = count + 1;
    },
    getValue() {
      return count;
    },
  };
}

const counter = createCounter();

counter.increment();

console.log(counter.count);
console.log(counter.getValue());

Phân tích

Output là 0 rồi 1.

Property count trong returned object nhận giá trị tại thời điểm object được tạo. Nó không phải alias tự động tới lexical binding count. Method increment() update captured binding, còn counter.count vẫn giữ primitive value ban đầu.

Đây là bug rất dễ gặp khi cố expose "state để tiện debug".

Edge Case 2 — Getter trả object reference ​

js
function createSettings() {
  const settings = {
    theme: "dark",
  };

  return {
    getSettings() {
      return settings;
    },
  };
}

Phân tích

Binding settings private, nhưng caller nhận chính object reference qua getSettings(). Caller có thể viết getSettings().theme = "light".

Nếu invariant yêu cầu caller không được mutate object trực tiếp, API cần thiết kế boundary khác. Lesson này không đi sâu vào immutable data architecture; mục tiêu là phân biệt private binding với private mutable object.

Edge Case 3 — Public method có thể phá invariant ​

js
function createCounter() {
  let count = 0;

  return {
    setValue(nextValue) {
      count = nextValue;
    },
    getValue() {
      return count;
    },
  };
}

Phân tích

State vẫn private về lexical access, nhưng API lại cho phép caller đặt bất kỳ value nào. Encapsulation không tự động tạo invariant tốt. Public API phải được thiết kế theo những mutation mà system thực sự muốn cho phép.

10. Debug Lab (Bài lab gỡ lỗi) ​

Debug Lab — Public count và private count lệch nhau

Symptom (Triệu chứng): Developer mong đợi counter.count và counter.getValue() luôn giống nhau, nhưng sau increment() chúng trả kết quả khác nhau.

Reproduction (Tái hiện lỗi):

js
function createCounter() {
  let count = 0;

  return {
    count,
    increment() {
      count = count + 1;
    },
    getValue() {
      return count;
    },
  };
}

const counter = createCounter();

counter.increment();

console.log(counter.count); // 0
console.log(counter.getValue()); // 1

Evidence (Bằng chứng):

  • Có lexical binding count bên trong createCounter.
  • Returned object cũng có property tên count.
  • increment() reassign lexical binding.
  • Không có code nào update counter.count.

Hypothesis (Giả thuyết):

  • Property count và lexical binding count là hai storage locations khác nhau.
  • Object property nhận value 0 khi object được tạo.
  • Closure methods tiếp tục đọc/ghi lexical binding.

Verification (Xác minh):

js
counter.count = 999;

console.log(counter.count); // 999
console.log(counter.getValue()); // 1

Nếu hai giá trị tiếp tục độc lập, giả thuyết được xác nhận.

Root Cause (Nguyên nhân gốc rễ):

Developer tưởng:

text
object.count
=
alias tới private binding count

Nhưng thực tế:

text
private binding count
≠
public property counter.count

Fix (Sửa):

Không expose snapshot property nếu source of truth phải là private binding:

js
function createCounter() {
  let count = 0;

  return {
    increment() {
      count = count + 1;
    },
    decrement() {
      count = count - 1;
    },
    getValue() {
      return count;
    },
  };
}

Prevention (Phòng ngừa):

  • Xác định một source of truth duy nhất cho state.
  • Không duplicate private state thành public primitive property nếu không có synchronization strategy.
  • Khi expose read access, ưu tiên method/getter có contract rõ hơn snapshot dễ stale.
  • Trong code review, hỏi: "Property này là source of truth hay chỉ là copy?"

11. Design Exercise (Bài tập thiết kế giải pháp) ​

Design Exercise — Private State hay Public State?

Bạn cần thiết kế một createVolumeController().

Requirement:

text
increase()
decrease()
getVolume()

Constraints:

  • Volume bắt đầu ở 50.
  • Volume luôn nằm trong khoảng 0..100.
  • Caller không được bypass giới hạn bằng cách gán trực tiếp.
  • API phải đủ nhỏ để dễ reasoning.
  • Chưa dùng class, prototype hoặc library.

Có ba options:

Option A — Public property

js
const controller = {
  volume: 50,
};

Option B — Private binding + unrestricted setter

text
setVolume(value)
getVolume()

Option C — Private binding + domain operations

text
increase()
decrease()
getVolume()

Hãy quyết định dựa trên:

text
Context
→ Constraints
→ Options
→ Decision
→ Trade-offs
[Một quyết định tham khảo]

Context: Volume phải luôn nằm trong range 0..100.

Constraints: Caller không được bypass invariant bằng direct assignment.

Options:

  • A đơn giản nhưng state hoàn toàn public.
  • B private về lexical access, nhưng unrestricted setter vẫn cho phép API nhận invalid value nếu validation không chặt.
  • C giới hạn mutation thành đúng operations mà domain cần.

Decision: Chọn Option C.

js
function createVolumeController() {
  let volume = 50;

  return {
    increase() {
      if (volume < 100) {
        volume = volume + 1;
      }
    },
    decrease() {
      if (volume > 0) {
        volume = volume - 1;
      }
    },
    getVolume() {
      return volume;
    },
  };
}

Trade-off: API ít flexible hơn setVolume(value), nhưng đổi lại invariant dễ bảo vệ và behavior dễ reasoning hơn. Nếu requirement sau này cần set trực tiếp, có thể thêm operation có validation rõ ràng thay vì expose binding.

12. Production Scenario (Tình huống thực tế) ​

Production Scenario — Retry Budget của một operation

Một module có rule:

Một operation chỉ được retry tối đa 3 lần.

Implementation yếu:

js
const retryState = {
  attempts: 0,
};

Bất kỳ code nào giữ reference đều có thể:

js
retryState.attempts = -100;
retryState.attempts = 999;

Ta muốn public API chỉ cho phép:

text
recordAttempt()
canRetry()
getAttempts()

Một closure-based implementation đơn giản:

js
function createRetryBudget(maxAttempts) {
  let attempts = 0;

  return {
    recordAttempt() {
      if (attempts < maxAttempts) {
        attempts = attempts + 1;
      }
    },
    canRetry() {
      return attempts < maxAttempts;
    },
    getAttempts() {
      return attempts;
    },
  };
}

Context: Nhiều nơi trong code cần kiểm tra retry budget.

Constraint: Caller không được tự ý sửa counter thành một value phá rule.

Decision: Giữ attempts trong lexical environment và chỉ expose operations có semantic rõ ràng.

Production value: Private state không chỉ để "giấu biến". Nó giúp giảm số đường có thể đưa state vào trạng thái invalid.

Code Review Lens

Khi review mutable state, đừng chỉ hỏi:

"Biến này có private không?"

Hãy hỏi thêm:

"Những operation nào được phép thay đổi nó, và API hiện tại có cho caller bypass invariant không?"

13. AI-assisted Exercise (Bài tập với AI) ​

Level C — Delegate & Inspect (Ủy quyền và kiểm tra)

  1. Tự thiết kế trước: Viết createCounter() có invariant count >= 0.
  2. Hỏi AI: Yêu cầu AI tạo 3 implementation:
    • public property;
    • closure private state;
    • closure private state + setValue.
  3. Inspect: Với từng implementation, xác định:
    • state nằm ở đâu;
    • mutation points là gì;
    • caller có bypass invariant không;
    • có duplicate source of truth không.
  4. Challenge: Nếu AI nói "closure làm state immutable" hoặc "private state luôn an toàn", yêu cầu AI đưa counterexample.
  5. Verify: Tự viết test cố phá invariant.

Gợi ý

Mục tiêu không phải chọn solution "trông hiện đại nhất".

Bạn phải đánh giá bằng mental model:

text
Who owns state?
Who can mutate it?
Through which API?
Which invariant is protected?
Đáp án tham khảo
  • Public property: dễ đọc/ghi nhưng caller có thể bypass invariant trực tiếp.
  • Closure private state: giới hạn direct access; mutation đi qua methods được return.
  • Closure + unrestricted setValue: binding vẫn private nhưng API có thể cho phép invalid state nếu setter không validate.
  • Kết luận: Closure là cơ chế giúp tạo boundary. Chất lượng invariant vẫn phụ thuộc cách bạn thiết kế public API.

14. Teach Back (Dạy lại) ​

Giải thích cho một đồng nghiệp junior trong 2 phút:

"Closure tạo private state như thế nào, và tại sao private state không tự động đồng nghĩa với immutable hoặc safe state?"

Yêu cầu:

  • Dùng đúng terminology: lexical environment, binding, closure, public API, invariant.
  • Giải thích được vì sao counter.count không truy cập được private binding nếu property không được expose.
  • Giải thích được vì sao nhiều methods vẫn cùng mutate một binding.
  • Đưa ít nhất một counterexample chứng minh private binding chưa đủ để bảo vệ mọi invariant.
Mô phỏng
  • Bạn nói:
    • Khi createCounter() chạy, nó tạo một lexical environment có binding count.
    • Các methods increment, decrement, getValue được tạo trong environment đó và giữ reference cần thiết để resolve count.
    • Outer function return object chứa methods, nhưng không return một public property trỏ tới binding count. Vì vậy caller chỉ có API methods, không có identifier trực tiếp để reassign private binding.
    • increment và decrement vẫn có thể mutate count vì private không có nghĩa immutable. Chúng là những mutation points mà API chủ động cho phép.
    • Nếu API return chính một mutable object reference hoặc expose một unrestricted setter, caller vẫn có thể làm state invalid. Closure chỉ giúp tạo access boundary; invariant còn phụ thuộc design của operations.

💡 Hình dung state nằm trong một phòng khóa. Closure methods là những cửa có kiểm soát. Khóa cửa chính không có nghĩa mọi cửa phụ đều an toàn — ta vẫn phải thiết kế từng cửa cho đúng rule.

Gợi ý đánh giá bản thân

  • Bạn có chỉ ra được binding nào thật sự private không?
  • Bạn có phân biệt private binding với public object property cùng tên không?
  • Bạn có giải thích được tại sao getter trả object reference có thể làm mất mutation control không?
  • Bạn có xác định được invariant và tất cả mutation points của API không?

15. Assessment (Đánh giá) ​

Objective (Mục tiêu)Hình thức đánh giá
Giải thích private state bằng ClosureTeach Back — environment → closure → API boundary
Implement createCounter()Lab 1 — đúng increment, decrement, getValue
Bảo vệ invariantLab 2 + Design Exercise
Dự đoán captured state behaviorPrediction — 3/3 đúng
Phân biệt private binding vs mutable objectPrediction 3 + Edge Case 2
Debug duplicate/stale public stateDebug Lab — xác định đúng hai storage locations
Thiết kế mutation boundaryDesign Exercise — Context → Constraints → Options → Decision

16. Exit Criteria (Tiêu chí qua bài) ​

  • [ ] Có thể giải thích private state bằng lexical environment và closure.
  • [ ] Implement được createCounter() với increment(), decrement(), getValue().
  • [ ] count không được expose trực tiếp bằng public property.
  • [ ] Dự đoán đúng 3/3 prediction scenarios.
  • [ ] Có thể chứng minh nhiều methods trong cùng API đang dùng cùng một captured binding.
  • [ ] Có thể phân biệt private binding với immutable state.
  • [ ] Có thể giải thích tại sao return object reference có thể cho caller mutate state gián tiếp.
  • [ ] Debug được bug public snapshot lệch khỏi private source of truth.
  • [ ] Có thể xác định tất cả mutation points và invariant của một API nhỏ.
  • [ ] Có thể thiết kế private-state API không cho caller bypass một invariant đơn giản.

17. Spiral Connection (Liên kết xoắn ốc) ​

Previous (Trước): Lesson 1.4.1 — Closure Formation và Lesson 1.4.2 — Closure Lifetime. Bạn đã biết function giữ outer environment reference và environment có thể tiếp tục reachable sau khi creator function return.

Current (Hiện tại): Closure làm Private State. Bạn dùng chính cơ chế đó để giữ mutable state trong lexical environment và chỉ expose những operations được phép truy cập state.

Next (Tiếp theo):

  • Lesson 1.4.4 — Factory Functions (tạo nhiều instance, mỗi instance có state riêng)
  • Lesson 1.4.5 — Closure trong Loop (binding nào được capture?)
  • Lesson 1.4.6–1.4.7 — Callback + Async (private/captured state sống qua thời gian)
  • Stage 8 — React state và stale closure
  • Stage 11 — Memory retention và debugging
📴 Offline Mode — Content served from cache