Module 01 — JavaScript & TypeScript Nền tảng

Phải có ⏱ 8-10 giờ 📋 Prerequisites: Không; nếu mới với RN, đọc Bridge 00A00B00C

🎯 Mục tiêu học tập

📖 Hướng dẫn học

1 Đọc JavaScript.infojavascript.info
Focus chapters: Closures, Prototypes, Promises, Async/Await, Event Loop.
Thời gian: ~3 giờ. Đọc kỹ, chạy code examples trong console.
2 Xem video bổ trợ
- "JavaScript: Understanding the Weird Parts" (Udemy — Tony Alicea) — giải thích execution context, scope chain
- "What the heck is the event loop?" (JSConf — Philip Roberts) — YouTube, miễn phí
Thời gian: ~2 giờ
3 Đọc TypeScript Handbooktypescriptlang.org/docs/handbook
Focus: Everyday Types, Narrowing, Functions, Object Types, Generics, Utility Types.
Thời gian: ~2 giờ
4 Làm mini exercise TypeScript cho React Native
Mục đích: luyện API response types, retry, upload queue, normalize data.
Thời gian: ~2-3 giờ

🧭 Vòng lặp học module này

📚 Lý thuyết chi tiết

1. Closures

Bản chất: Closure = function + lexical environment (biến ở scope cha tại thời điểm function được tạo). Khi 1 function được tạo bên trong function khác, nó "nhớ" được các biến của function cha — ngay cả khi function cha đã return.

Tại sao quan trọng cho RN dev:

Ví dụ counter closure:
function createCounter(initialValue: number) {
  let count = initialValue;
  return {
    increment: () => ++count,
    decrement: () => --count,
    getCount: () => count,
  };
}

const counter = createCounter(0);
counter.increment();
counter.increment();
console.log(counter.getCount());

Biến count không accessible từ bên ngoài — chỉ các methods returned mới access được. Đây là data privacy qua closure.

2. Prototype Chain

Bản chất: JavaScript không có class thật (trước ES6). Mỗi object có 1 internal link ([[Prototype]]) trỏ tới object khác. Khi truy cập property không có trên object → JS đi lên prototype chain tìm.

class syntax (ES6) chỉ là syntactic sugar — bên dưới vẫn là prototype-based.

const animal = { speak() { return "..." } };
const dog = Object.create(animal);
dog.bark = () => "Woof!";

dog.bark();
dog.speak();

dog.speak() hoạt động vì JS tìm lên prototype chain → thấy speakanimal.

3. Event Loop

Thứ tự thực thi:

  1. Call Stack — chạy synchronous code
  2. Microtask Queue — Promise callbacks (.then, .catch), queueMicrotask, MutationObserver
  3. Macrotask Queue — setTimeout, setInterval, I/O

Rule: Sau khi call stack trống → xử lý HẾT microtask queue → rồi mới lấy 1 macrotask.

Câu đố kinh điển:
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");

Output: 1 → 4 → 3 → 2. Vì: sync (1,4) → microtask (Promise: 3) → macrotask (setTimeout: 2).

4. Async/Await

async/await là syntactic sugar cho Promise. async function luôn return Promise. await pause execution cho tới khi Promise resolve.

Sequential vs Parallel:

const fetchUserAndPosts = async (userId: string) => {
  const user = await fetchUser(userId);
  const posts = await fetchPosts(userId);
  return { user, posts };
};

const fetchBothParallel = async (userId: string) => {
  const [user, posts] = await Promise.all([
    fetchUser(userId),
    fetchPosts(userId),
  ]);
  return { user, posts };
};

Version 1: sequential — chậm (fetch user xong mới fetch posts). Version 2: parallel — nhanh hơn (2 requests chạy cùng lúc).

5. ES6+ Features quan trọng

FeatureDùng khiVí dụ
DestructuringExtract values từ object/arrayconst { name, age } = user;
Spread/RestClone, merge, collect args{...obj, key: val}
Optional ChainingSafe property accessuser?.address?.city
Nullish CoalescingDefault value (chỉ null/undefined)value ?? 'default'
Template LiteralsString interpolation`Hello ${name}`
Map/SetUnique values, key-value pairsnew Set([1,2,2,3])

6. TypeScript Core

Interface vs Type

Generics

function getFirstElement<T>(arr: T[]): T | undefined {
  return arr[0];
}

const num = getFirstElement([1, 2, 3]);
const str = getFirstElement(["a", "b"]);

Utility Types

TypeMô tảUse case
Partial<T>Tất cả props optionalUpdate functions
Required<T>Tất cả props requiredValidation
Pick<T, K>Chọn subset propsAPI responses
Omit<T, K>Loại bỏ propsForm data (bỏ id)
Record<K, V>Key-value mappingLookup objects
Exclude<T, U>Loại type khỏi unionFilter types

Discriminated Unions

type Result<T> =
  | { status: "success"; data: T }
  | { status: "error"; error: string }
  | { status: "loading" };

function handleResult(result: Result<User>) {
  switch (result.status) {
    case "success":
      return result.data;
    case "error":
      throw new Error(result.error);
    case "loading":
      return null;
  }
}

TypeScript tự narrow type dựa trên status field — rất hữu ích cho API response handling trong RN.

7. Array Methods

MethodReturnDùng khi
map()New array (same length)Transform mỗi element
filter()New array (subset)Lọc theo điều kiện
reduce()Single valueTổng hợp, group, flatten
find()First match | undefinedTìm 1 element
some()booleanCó ít nhất 1 match?
every()booleanTất cả đều match?
flatMap()New array (flattened 1 level)Map + flatten

❓ Câu hỏi phỏng vấn + Đáp án

Q1: Closure là gì? Cho ví dụ thực tế trong React Native.

Closure là khi 1 function "nhớ" được các biến ở lexical scope cha, ngay cả khi function cha đã return. Về mặt kỹ thuật: closure = function + reference tới lexical environment.

Ví dụ thực tế: React hooks chính là closures. Mỗi lần component render, function component chạy lại, tạo ra 1 closure mới "close over" state values tại render đó. Đây là lý do tại sao state trong useEffect callback có thể "stale" — nó nhớ giá trị lúc closure được tạo.

Ví dụ khác: useDebounce hook — closure giữ reference tới timeout ID giữa các lần gọi.

Q2: Output của đoạn code sau? Giải thích.
console.log("A");
setTimeout(() => console.log("B"), 0);
Promise.resolve().then(() => console.log("C"));
Promise.resolve().then(() => setTimeout(() => console.log("D"), 0));
console.log("E");

Output: A → E → C → B → D

Giải thích:

  • A, E: synchronous — chạy ngay trên call stack
  • C: microtask (Promise) — chạy sau sync, trước macrotask
  • B: macrotask (setTimeout) — chạy sau tất cả microtasks
  • D: macrotask được schedule bởi microtask — chạy cuối cùng
Q3: this trong arrow function vs regular function?

Arrow function: không có this riêng — kế thừa this từ lexical scope (enclosing function/context). Không thể bind/call/apply để thay đổi.

Regular function: this phụ thuộc vào cách gọi (dynamic binding). Gọi qua object → this = object. Gọi standalone → this = undefined (strict) hoặc window.

Trong React Native: Hầu hết dùng arrow functions vì không cần dynamic this. Class components dùng arrow methods để auto-bind.

Q4: Promise.all vs Promise.allSettled vs Promise.race?
MethodResolve khiReject khiUse case
Promise.allTất cả resolveBất kỳ 1 cái rejectParallel requests, tất cả cần thành công
Promise.allSettledTất cả settled (resolve hoặc reject)Không bao giờ rejectParallel requests, muốn biết kết quả từng cái
Promise.raceCái đầu tiên settleCái đầu tiên rejectTimeout pattern, fastest response
Q5: Deep copy vs Shallow copy — implement deep clone?

Shallow copy: copy 1 level — nested objects vẫn share reference (Object.assign, spread {...obj}).

Deep copy: copy tất cả nested levels — hoàn toàn independent.

Cách implement:

  • structuredClone(obj) — native, modern (recommended)
  • JSON.parse(JSON.stringify(obj)) — hack, mất functions/undefined/Date/RegExp
  • Recursive function — handle edge cases
Q6: var vs let vs const — hoisting behavior?
ScopeHoistingRe-assignTDZ
varFunctionCó (initialized = undefined)Không
letBlockCó (nhưng TDZ)
constBlockCó (nhưng TDZ)Không

TDZ (Temporal Dead Zone): biến đã được hoisted nhưng chưa initialized → access sẽ throw ReferenceError.

Best practice: Luôn dùng const by default, let khi cần re-assign. Tránh var.

Q7: interface vs type trong TypeScript — khi nào dùng cái nào?

interface: extends, declaration merging, IDE experience tốt hơn cho objects. Dùng cho: component props, API response shapes, class contracts.

type: union types, intersection types, mapped types, conditional types. Dùng cho: union types, utility types, complex type transformations.

Trong thực tế RN: Dùng interface cho Props types và API models. Dùng type cho unions (navigation params, action types).

Q8: Viết 1 generic function type-safe — ví dụ?
function groupBy<T, K extends string>(
  items: T[],
  keyGetter: (item: T) => K
): Record<K, T[]> {
  const result = {} as Record<K, T[]>;
  for (const item of items) {
    const key = keyGetter(item);
    if (!result[key]) result[key] = [];
    result[key].push(item);
  }
  return result;
}

const grouped = groupBy(users, (u) => u.role);

TypeScript tự infer types từ usage — grouped sẽ có type Record<string, User[]>.

Q9: WeakMap/WeakSet dùng khi nào?

WeakMap: keys phải là objects, keys được held weakly (GC có thể collect nếu không có reference khác). Dùng cho: cache metadata cho DOM nodes/objects mà không prevent garbage collection. Private data pattern.

WeakSet: values phải là objects, held weakly. Dùng cho: tracking đã process object nào chưa.

Khác Map/Set: Không iterable, không có .size — vì contents phụ thuộc vào GC timing.

Q10: 'use strict' là gì? Tại sao quan trọng?

Strict mode bật các quy tắc nghiêm ngặt hơn: không cho phép undeclared variables, không cho delete variables, this = undefined trong standalone functions (thay vì window), cấm duplicate params, etc.

Trong RN: ES modules (import/export) luôn ở strict mode. Class bodies cũng strict mode. Nên không cần khai báo thủ công 'use strict'.

🧩 Mini exercise JS/TS thực tế cho React Native

Exercise 1: Typed API response
Tạo ApiResult<T> dạng discriminated union cho success/error. Viết function nhận response này và render đúng loading/success/error state mà không dùng any.
Exercise 2: Retry với backoff
Implement retry(fn, { retries, delayMs }). Chỉ retry lỗi network/5xx, không retry 400/401/422.
Exercise 3: Upload queue
Implement queue chạy tối đa 3 upload cùng lúc, giữ thứ tự result, có cancel flag. Đây là pattern gặp khi upload nhiều ảnh từ mobile.
Exercise 4: Normalize nested response
Từ response posts có author/comment lồng nhau, transform thành { postsById, usersById, commentsById } để dùng trong state/cache.

🏋️ Bài tập thực hành

Bài 1: Closure Counter Module
Viết 1 module createStore dùng closure — có methods: getState(), setState(newState), subscribe(listener). Listener được gọi mỗi khi state thay đổi. Đây chính là mini Redux.
Bài 2: Promise Utility Functions
Implement 3 functions:
- promiseAll<T>(promises: Promise<T>[]): Promise<T[]>
- promiseRace<T>(promises: Promise<T>[]): Promise<T>
- retry<T>(fn: () => Promise<T>, maxRetries: number): Promise<T>
Bài 3: TypeScript API Types
Định nghĩa type system cho 1 REST API:
- ApiResponse<T> (success/error discriminated union)
- PaginatedResponse<T> (data + pagination metadata)
- User model với Partial cho update, Omit cho create (bỏ id)

⚠️ Lỗi thường gặp

🏢 Case đi làm thật

Case: API trả { data, error } không rõ success hay fail. UI vừa đọc data.name vừa show error, thỉnh thoảng crash khi data null.

Cách xử lý: Dùng discriminated union: { status: 'success', data } hoặc { status: 'error', error }. TypeScript sẽ buộc UI xử lý đủ nhánh.

🧠 Ghi nhớ nhanh

JS/TS nền tảng có ích cho RN khi nó giúp bạn xử lý async, type API, closure và immutable data đúng trong app thật.

❔ Câu hỏi tự kiểm tra

1. Khi nào dùng Promise.allSettled thay vì Promise.all?
2. Discriminated union giúp UI state an toàn hơn thế nào?
3. Stale closure thường xuất hiện ở đâu trong RN?
4. Vì sao update object/array nên tạo reference mới?

✅ Checklist tự đánh giá

📎 Tài nguyên

📘 JavaScript.info — Tài liệu JS toàn diện nhất, miễn phí
📘 TypeScript Handbook — Official docs
📺 "What the heck is the event loop?" — Philip Roberts, JSConf (YouTube)
📝 overreacted.io — Dan Abramov's blog (closures trong React)
📺 Fireship — YouTube channel, short concise videos
📺 Web Dev Simplified — YouTube, JS/TS deep dives
📘 Kent C. Dodds Blog — Testing, React patterns