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:
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.
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 speak ở animal.
Thứ tự thực thi:
Rule: Sau khi call stack trống → xử lý HẾT microtask queue → rồi mới lấy 1 macrotask.
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).
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).
| Feature | Dùng khi | Ví dụ |
|---|---|---|
| Destructuring | Extract values từ object/array | const { name, age } = user; |
| Spread/Rest | Clone, merge, collect args | {...obj, key: val} |
| Optional Chaining | Safe property access | user?.address?.city |
| Nullish Coalescing | Default value (chỉ null/undefined) | value ?? 'default' |
| Template Literals | String interpolation | `Hello ${name}` |
| Map/Set | Unique values, key-value pairs | new Set([1,2,2,3]) |
interface: extends được, declaration merging, dùng cho object shapestype: union/intersection types, mapped types, dùng cho mọi loại typeinterface cho object shapes, type cho unions/intersections/aliasesfunction getFirstElement<T>(arr: T[]): T | undefined {
return arr[0];
}
const num = getFirstElement([1, 2, 3]);
const str = getFirstElement(["a", "b"]);
| Type | Mô tả | Use case |
|---|---|---|
Partial<T> | Tất cả props optional | Update functions |
Required<T> | Tất cả props required | Validation |
Pick<T, K> | Chọn subset props | API responses |
Omit<T, K> | Loại bỏ props | Form data (bỏ id) |
Record<K, V> | Key-value mapping | Lookup objects |
Exclude<T, U> | Loại type khỏi union | Filter types |
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.
| Method | Return | Dùng khi |
|---|---|---|
map() | New array (same length) | Transform mỗi element |
filter() | New array (subset) | Lọc theo điều kiện |
reduce() | Single value | Tổng hợp, group, flatten |
find() | First match | undefined | Tìm 1 element |
some() | boolean | Có ít nhất 1 match? |
every() | boolean | Tất cả đều match? |
flatMap() | New array (flattened 1 level) | Map + flatten |
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.
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:
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.
| Method | Resolve khi | Reject khi | Use case |
|---|---|---|---|
Promise.all | Tất cả resolve | Bất kỳ 1 cái reject | Parallel requests, tất cả cần thành công |
Promise.allSettled | Tất cả settled (resolve hoặc reject) | Không bao giờ reject | Parallel requests, muốn biết kết quả từng cái |
Promise.race | Cái đầu tiên settle | Cái đầu tiên reject | Timeout pattern, fastest response |
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| Scope | Hoisting | Re-assign | TDZ | |
|---|---|---|---|---|
var | Function | Có (initialized = undefined) | Có | Không |
let | Block | Có (nhưng TDZ) | Có | Có |
const | Block | Có (nhưng TDZ) | Không | Có |
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.
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).
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[]>.
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.
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'.
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.
retry(fn, { retries, delayMs }). Chỉ retry lỗi network/5xx, không retry 400/401/422.
{ postsById, usersById, commentsById } để dùng trong state/cache.
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.
promiseAll<T>(promises: Promise<T>[]): Promise<T[]>promiseRace<T>(promises: Promise<T>[]): Promise<T>retry<T>(fn: () => Promise<T>, maxRetries: number): Promise<T>
ApiResponse<T> (success/error discriminated union)PaginatedResponse<T> (data + pagination metadata)User model với Partial cho update, Omit cho create (bỏ id)
any cho API response, mất type narrowing ở UI.null và undefined, validation/form bị sai case.Promise.all cho batch upload, một item fail làm hỏng toàn bộ flow dù có thể retry từng item.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.
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.
Promise.allSettled thay vì Promise.all?