Tổng hợp 165 câu hỏi phỏng vấn JavaScript kèm đáp án chi tiết, song ngữ Việt–Anh — sắp xếp từ cơ bản đến nâng cao. Ôn luyện miễn phí, tự động lưu tiến độ.
var, let và const đều khai báo biến trong JavaScript, nhưng khác nhau về scope, hành vi khai báo lại, và khả năng gán lại giá trị. - var có function scope — biến "lan" ra toàn bộ function chứa nó, bất kể nó được…
JavaScript có 7 kiểu nguyên thuỷ: string, number, boolean, null, undefined, symbol, bigint. Đặc điểm chung: bất biến và sao chép theo giá trị. Không phải nguyên thuỷ: object, array, function, Date, Map, Set — tất cả đều là object và sao chép theo tham…
Symbol là kiểu dữ liệu nguyên thuỷ tạo ra một giá trị duy nhất (unique). Mỗi lần gọi Symbol() sẽ tạo ra một giá trị hoàn toàn khác biệt, ngay cả khi có cùng mô tả (description). Ứng dụng chính: - Tránh xung đột tên…
== so sánh sau khi ép kiểu; === so sánh cả giá trị lẫn kiểu, không ép. Bảng ép kiểu của == có nhiều trường hợp phản trực giác, đó là lý do gần như mọi bộ quy tắc lint đều bật eqeqeq. Ngoại lệ…
Type coercion là việc JavaScript tự đổi kiểu khi phép toán nhận hai kiểu khác nhau. Hai luật chi phối gần hết các trường hợp: 1. + ưu tiên chuỗi — chỉ cần một toán hạng là chuỗi thì thành nối chuỗi. 2. Mọi toán…
Falsy là giá trị bị coi là false khi đưa vào ngữ cảnh boolean. Có đúng 8 giá trị, học thuộc là đủ: Mọi thứ còn lại đều truthy — kể cả những thứ trông như "rỗng": Lưu ý — số 0 và chuỗi rỗng…
NaN (Not a Number) là kết quả của một phép toán số không ra kết quả hợp lệ: Theo chuẩn IEEE 754, NaN không bằng bất cứ giá trị nào — kể cả chính nó. Đây là quy định của chuẩn số học dấu chấm…
Hoisting là cách JavaScript engine tạo binding cho các khai báo trước khi thực thi code từng dòng trong scope. Bạn có thể hình dung như ban tổ chức lập danh sách chỗ ngồi trước khi khách đến. Tên đã có trong danh sách, nhưng…
TDZ là khoảng từ đầu block tới dòng khai báo của một biến let/const. Trong khoảng đó biến đã tồn tại nhưng chưa khởi tạo, truy cập sẽ ném ReferenceError. Dòng typeof x là điểm hay được hỏi: typeof vốn an toàn với biến chưa…
Scope chain là cơ chế JavaScript tìm kiếm biến từ scope hiện tại ra ngoài đến global scope. Khi không tìm thấy biến trong scope hiện tại, JS tìm trong scope cha, rồi tiếp tục lên cho đến global. Nếu không tìm thấy ở global,…
Closure là hàm 'nhớ' được biến từ lexical scope bên ngoài, ngay cả sau khi hàm ngoài đã return. Hàm trả về vẫn truy cập count. Ứng dụng thực tế: private variables (encapsulation), factory functions, event handlers giữ state, debounce/throttle, React hooks (useState bên trong…
Khi dùng var trong vòng lặp for, tất cả callback chia sẻ cùng tham chiếu đến biến i — vì var là function scope, chỉ có một biến i duy nhất. Đây là câu hỏi phỏng vấn cổ điển về closure.
Function declaration được hoisted hoàn toàn (cả tên và thân hàm), có thể gọi trước khi khai báo. Function expression chỉ hoisted phần khai báo biến (với var là undefined), không thể gọi trước khi gán.
Ba cách viết hàm, khác nhau ở hoisting, this, và những gì hàm có sẵn. declaration expression arrow ------------ Hoist cả thân hàm có không không this riêng có có không (lấy từ scope ngoài) arguments có có không Dùng với new được được không…
Callback là hàm truyền vào một hàm khác để được gọi lại sau. JavaScript chạy một luồng. Nếu phải chờ mạng hay đĩa theo kiểu chặn, cả giao diện đứng im. Callback cho phép đăng ký "làm việc này khi có kết quả" rồi trả…
Higher-order function (HOF) là hàm nhận hàm làm tham số, trả về hàm, hoặc cả hai. HOF có mặt ở khắp nơi trong thực tế: - map, filter, reduce, sort, forEach - middleware Express: (req, res, next) = {} - useMemo, useCallback, HOC trong React…
IIFE là hàm được định nghĩa và gọi ngay lập tức. Cặp ngoặc bọc ngoài là bắt buộc: nó biến function declaration thành function expression, để có thể gọi ngay. Trước ES6, JavaScript chỉ có function scope — IIFE là cách duy nhất để tạo…
Pure function thoả hai điều kiện: 1. Cùng input luôn cho cùng output. 2. Không có side effect — không sửa gì bên ngoài phạm vi của nó. Lợi ích thực tế: - Test không cần mock — gọi hàm, so kết quả, hết. -…
...args gom mọi tham số còn lại thành một Array thật. So với arguments: ...rest arguments --------- Kiểu Array thật array-like, phải Array.from() Nội dung chỉ phần chưa đặt tên toàn bộ tham số Trong arrow function có không Đặt tên được có không Đừng…
Function composition là ghép nhiều hàm nhỏ thành một hàm lớn, đầu ra của hàm này là đầu vào của hàm kế tiếp. Hai cái chỉ khác chiều đọc: Lợi ích so với biến trung gian: Gặp ở đâu trong thực tế: applyMiddleware của Redux,…
Callback hell là khi các callback bất đồng bộ lồng vào nhau nhiều tầng, code thụt lề thành hình kim tự tháp và code xử lý lỗi bị lặp lại ở mọi tầng. Hai vấn đề thật, không chỉ là xấu: lỗi phải xử lý…
this không phụ thuộc nơi hàm được định nghĩa mà phụ thuộc cách hàm được gọi. Bốn quy tắc, xét theo thứ tự ưu tiên giảm dần: Arrow function không nằm trong bốn quy tắc này — nó không có this riêng mà lấy từ…
Trong strict mode, this ở hàm thông thường là undefined thay vì global object (window/global). Điều này giúp phát hiện lỗi khi vô tình gọi hàm như function thay vì method. Kích hoạt bằng 'use strict'; ở đầu file hoặc hàm. ES modules tự động…
Cả ba đều đặt this một cách tường minh, khác nhau ở cách truyền tham số và thời điểm gọi. Gọi ngay Tham số Trả về ------------ call có rời từng cái kết quả hàm apply có một mảng kết quả hàm bind không rời…
Arrow function không có this riêng, kế thừa this từ lexical scope nơi nó được định nghĩa. Khi dùng làm method, this không trỏ đến object chứa method. Dùng function thông thường cho methods, arrow function cho callbacks bên trong method.
Gọi new Foo() khiến JavaScript làm bốn việc theo thứ tự: Tự cài lại new để thấy rõ: Quy tắc ở bước 4 hay bị hỏi: return giá trị nguyên thuỷ thì bị bỏ qua, chỉ return object mới ghi đè được this. Lưu ý:…
Trong class, this trong constructor và methods trỏ đến instance được tạo. Tuy nhiên, khi truyền method như callback, mất this binding. Giải pháp: dùng arrow function trong class fields (tự bind), bind trong constructor, hoặc .bind() khi truyền callback.
Prototype chain là đường JavaScript đi tìm thuộc tính. Nếu obj.name không có trên obj, JS tìm tiếp trên prototype... cho tới khi gặp null thì dừng.
proto là instance link — nằm trên object instance. prototype là blueprint — nằm trên constructor function. Khi dùng new Foo(), object mới có proto trỏ tới Foo.prototype. Trong code thực tế, dùng Object.getPrototypeOf(obj) thay vì truy cập proto trực tiếp.
Object.create(proto) tạo object mới với prototype là proto. Object con dùng được method từ proto qua prototype chain.
obj instanceof Foo kiểm tra: Foo.prototype có nằm trong prototype chain của obj không. Lưu ý: qua iframe/vm khác realm, instanceof có thể cho kết quả không như mong đợi.
Destructuring cho phép tách giá trị từ array/object ra từng biến. Hỗ trợ default values, alias, và nested destructuring.
Spread trải iterable ra thành từng phần tử (dùng trong function calls, array literals, object literals). Rest thu thập nhiều phần tử thành array (dùng trong function parameters). Cú pháp giống nhau nhưng ngữ cảnh ngược nhau.
Template literals dùng backtick thay dấu nháy, hỗ trợ: interpolation ${expression}, multi-line strings, tagged templates. Tagged templates là function nhận template strings và expressions làm arguments.
Class là cú pháp OOP rõ ràng hơn để tạo objects. Khác function constructor: class không thể gọi không có new (throw TypeError), methods không enumerable (không xuất hiện trong for...in), và phải khai báo trước khi dùng (không hoisted). Bên dưới vẫn dùng prototype…
Private field khai báo bằng tiền tố , chỉ truy cập được bên trong thân class. Đây là private thật ở tầng engine, khác hẳn quy ước name: Kiểm tra một object có phải instance có field đó không — cú pháp in cho private…
for...in duyệt key, for...of duyệt value. for...in for...of --------- Duyệt key enumerable value của iterable Đi lên prototype chain có không Dùng với object thường Array, String, Map, Set, NodeList, generator Object thường được TypeError — không iterable Hai lý do không nên dùng for...in…
Khác nhau ở kiểu key và quan hệ với garbage collector. Map WeakMap --------- Key bất kỳ kiểu nào chỉ object / symbol Duyệt được có không size có không Giữ key sống có không (weak reference) Ý nghĩa của "weak": key bị mất mọi…
Set là tập hợp giá trị không trùng lặp; WeakSet là bản chỉ chứa object và giữ tham chiếu yếu. So sánh trong Set dùng SameValueZero, nên hành vi với NaN khác ===: WeakSet chỉ có add/has/delete, không size, không duyệt: Chọn thế nào: cần…
ES module là hệ thống module chuẩn của JavaScript, chạy được cả trên browser lẫn Node. Bốn đặc điểm cần nhớ: 1. Phân tích tĩnh — import/export được xác định lúc phân tích cú pháp, không phải lúc chạy. Đây là điều kiện để bundler…
Proxy bọc object và chặn các operation (get, set, has, deleteProperty...) bằng handler traps. Reflect cung cấp methods tương ứng với các traps. Dùng để tạo reactive objects (Vue 3), validation, logging, lazy initialization, hay implement Observable.
Cả hai đều shallow copy. Object.assign() copy vào object đích hiện có, trigger setters trên target. Spread tạo plain object mới, không trigger setters. Object.assign() copy enumerable own properties bao gồm cả Symbol. Với nested objects, cả hai chỉ copy reference, không deep copy.
Nhóm method chuỗi thêm từ ES6 trở đi, thay được phần lớn regex đơn giản: Hai điểm hay bị hỏi thêm: 1. includes phân biệt hoa thường — muốn không phân biệt phải tự chuẩn hoá: 2. replaceAll với chuỗi thì an toàn, với regex…
at(index) truy cập phần tử và hỗ trợ chỉ số âm. So với cách viết cũ: Vì sao arr[-1] không chạy: trong JavaScript, chỉ số của mảng thực chất là key kiểu chuỗi của object. arr[-1] tra key '-1', một thuộc tính thường không tồn…
Promise là object đại diện cho kết quả mà tác vụ async sẽ trả về sau (ví dụ gọi API). Nó có 3 trạng thái: - pending: đang chờ - fulfilled: thành công (resolve) - rejected: thất bại (reject) Một khi đã fulfilled hoặc rejected…
- .then(...): chạy khi Promise thành công. - .catch(...): chạy khi có lỗi ở Promise trước đó. - .finally(...): luôn chạy sau cùng (dù thành công hay lỗi). Ví dụ: Mẹo cho người mới: dùng finally để dọn trạng thái UI như loading.
Cả hai đều là hàng đợi công việc bất đồng bộ, nhưng microtask được ưu tiên tuyệt đối. Microtask Macrotask --------- Nguồn .then/.catch/.finally, await, queueMicrotask, MutationObserver setTimeout, setInterval, sự kiện I/O, sự kiện DOM Khi nào chạy hết sạch queue sau mỗi macrotask một cái…
async/await là cú pháp viết Promise theo kiểu gần giống code đồng bộ, nên dễ đọc hơn chain .then(). Ví dụ: Điểm cần nhớ: - await chỉ dùng trong hàm async - lỗi nên bắt bằng try/catch - bản chất bên dưới vẫn là Promise.
Dùng try/catch bọc await expressions. Lỗi từ rejected Promise được catch như exception.
Không. setTimeout(fn, 0) đưa callback vào macrotask queue, nên nó chỉ chạy sau khi call stack rỗng và toàn bộ microtask đã xử lý xong. Ngoài ra spec HTML quy định độ trễ tối thiểu 4ms khi timer lồng từ tầng thứ 5 trở đi:…
Race condition xảy ra khi nhiều tác vụ bất đồng bộ chạy chồng nhau và kết quả phụ thuộc vào cái nào về trước — thứ tự đó không được đảm bảo. Trường hợp thường gặp: ô tìm kiếm gõ nhanh, phản hồi cũ về…
Unhandled rejection là khi một Promise bị reject mà không có handler nào bắt — không .catch(), không try/catch quanh await. Hậu quả khác nhau theo môi trường: Hành vi mặc định ------ Node 15+ kết thúc tiến trình (đổi được bằng --unhandled-rejections=warn) Browser ghi…
DOM (Document Object Model) là biểu diễn dạng cây của HTML document, mỗi element là một node. JavaScript tương tác qua document API: querySelector, createElement, appendChild, setAttribute... DOM manipulation là synchronous, thay đổi DOM ngay lập tức reflect trên page.
Khác nhau ở cách chọn, kiểu trả về, và có cập nhật theo DOM hay không. Trả về Sống / tĩnh Duyệt bằng forEach ------------ getElementById Element hoặc null — — querySelector Element hoặc null — — querySelectorAll NodeList tĩnh được getElementsBy HTMLCollection sống không…
Event delegation là gắn một listener trên phần tử cha thay vì gắn cho từng phần tử con, rồi dựa vào event bubbling để biết ai vừa được bấm. Ba lợi ích: 1. Ít bộ nhớ hơn — một listener thay vì hàng nghìn. 2.…
Hai việc hoàn toàn khác nhau: một cái chặn hành vi mặc định của browser, một cái chặn đường đi của sự kiện. preventDefault() stopPropagation() --------- Chặn hành vi mặc định (submit, theo link, tick checkbox) bubbling / capturing Sự kiện còn đi tiếp có…
fetch() là API HTTP dựa trên Promise, thay cho XMLHttpRequest. Điểm gây bất ngờ nhất — fetch không reject khi server trả lỗi: Đây là lý do mọi wrapper fetch trong dự án thật đều bắt đầu bằng if (!res.ok) throw .... fetch XMLHttpRequest ---------…
Web Worker chạy JavaScript trên một luồng riêng, nên tính toán nặng không làm đứng giao diện. Worker không truy cập được DOM, window, document. Nó có fetch, WebSocket, IndexedDB, crypto — đủ cho phần lớn việc xử lý dữ liệu. Dữ liệu qua postMessage…
IntersectionObserver báo bất đồng bộ khi một element đi vào (hoặc rời khỏi) viewport hay một container. Vì sao hơn scroll listener: callback không chạy trên đường cuộn, browser tự gộp nhiều element, và không có getBoundingClientRect() mỗi khung hình gây layout thrashing. Dùng cho:…
MutationObserver theo dõi thay đổi trên DOM tree (attribute changes, child additions/removals, text changes). Asynchronous, callbacks chạy như microtask. Hữu ích khi làm việc với third-party code thay đổi DOM, implement undo/redo, hay real-time updates. ResizeObserver theo dõi thay đổi kích thước element.
Ba nơi lưu dữ liệu ở client, khác nhau ở ai đọc được, tồn tại bao lâu, và có gửi lên server không. Cookie localStorage sessionStorage ------------ Dung lượng ~4KB ~5–10MB ~5–10MB Gửi kèm mọi request có không không JS đọc được có, trừ khi…
CustomEvent cho phép tạo events tuỳ chỉnh với data đính kèm. Dispatch với element.dispatchEvent(event). Bubbles và cancelable tuỳ chỉnh được. Dùng cho component communication không dùng framework, pub/sub pattern trong vanilla JS.
requestAnimationFrame(cb) xếp lịch cho callback chạy ngay trước lần vẽ tiếp theo của browser. Ba lý do hơn setTimeout(fn, 16): 1. Đồng bộ với nhịp vẽ thật của màn hình — đúng cả trên màn 120Hz, không bị lệch khung gây giật. 2. Tự dừng…
innerHTML: get/set HTML markup (nguy cơ XSS nếu set user input). textContent: get/set text của tất cả nodes kể cả ẩn, không trigger reflow. innerText: chỉ lấy text hiển thị (aware of CSS), trigger reflow để tính style. textContent nhanh nhất và an toàn nhất…
Debounce trì hoãn execution cho đến khi input ngừng, tránh gọi API quá nhiều lần. Ứng dụng: search input (gõ xong 300ms mới gọi API), resize handler, auto-save. Phỏng vấn thường hỏi viết tay để kiểm tra hiểu closure và timer.
Throttle giới hạn call rate xuống tối đa một lần mỗi interval — gọi ngay lần đầu rồi chặn. Khác debounce: throttle gọi ngay lần đầu rồi chặn, debounce đợi hết delay mới gọi. Dùng cho scroll, mousemove.
Deep clone sao chép đệ quy toàn bộ nested values mà không giữ reference gốc. Lưu ý: không handle Date, RegExp, Map, Set, circular refs. Production dùng structuredClone() hoặc lodash.
Flatten nested array bằng đệ quy reduce hoặc iterative spread. Phỏng vấn thường hỏi để test hiểu recursion và Array methods.
Promise.all resolve khi tất cả promises resolve; reject ngay khi có 1 promise reject. Lưu ý: handle empty array (resolve ngay []). Reject ngay khi 1 promise reject.
useLocalStorage persists state vào localStorage, tự đồng bộ khi value thay đổi. Test khả năng viết custom hooks với side effects.
useInfiniteScroll trigger callback khi sentinel element xuất hiện trong viewport. Gán ref cho sentinel element cuối danh sách.
Mini Redux dùng Context + useReducer: store dùng chung cho toàn app mà không cần thư viện ngoài. Phỏng vấn thường hỏi để test hiểu state management internals.
Exponential backoff retry một async call failing với delay tăng dần (1s, 2s, 4s...). Dùng cho API calls không ổn định. Delay tăng: 1s, 2s, 4s.
Deep equal so sánh đệ quy toàn bộ nested values — không dùng được === cho objects. Phỏng vấn thường hỏi để test recursion và edge cases (null, arrays).
Memoize caches kết quả function theo argument key, tránh tính toán lại những lần sau. Lưu ý: JSON.stringify chậm cho args lớn. Production dùng WeakMap cho object args, LRU cache giới hạn size.
FizzBuzz là bài toán kinh điển: lặp từ 1 đến n, in 'Fizz' nếu chia hết cho 3, 'Buzz' nếu chia hết cho 5, 'FizzBuzz' nếu chia hết cho cả hai, còn lại in số đó. Trường hợp biên cần xử lý: n bằng 0,…
LRU (Least Recently Used) Cache là cấu trúc dữ liệu loại bỏ phần tử ít được dùng gần đây nhất khi cache đầy. Trong JavaScript, Map vốn đã giữ thứ tự chèn, nên có thể dùng Map thay cho Doubly Linked List để triển khai…
forEach dùng để duyệt mảng và thực hiện side effect — không trả về mảng mới nên không tốn thêm bộ nhớ cấp phát. map tạo và trả về mảng mới cùng độ dài, tốn thêm bộ nhớ do phải cấp phát mảng — dùng…
Hai static method gom các phần tử của một iterable thành nhóm dựa trên giá trị callback trả về, thay cho việc tự viết reduce lồng nhau. - Object.groupBy(items, fn) → trả về object thường (null-prototype), key là string/symbol (giá trị callback bị ép về…
Promise.withResolvers() trả về một object gồm promise, resolve và reject cùng lúc, đưa hai hàm điều khiển ra ngoài phạm vi executor. Vấn đề với new Promise(executor): muốn resolve promise từ chỗ khác (event handler, callback của thư viện, hàng đợi message) bạn phải khai…
Array.fromAsync() tạo một mảng từ async iterable (hoặc sync iterable chứa promise), trả về một Promise. - Lặp tuần tự và await từng phần tử — khác Promise.all() chạy song song. Hợp khi nguồn là stream/paginated và bạn muốn xử lý lần lượt. - Hỗ…
ES2025 thêm các phép toán tập hợp ngay trên Set, thay cho việc tự convert sang array. - Trả về Set mới: union, intersection, difference, symmetricDifference. - Trả về boolean: isSubsetOf, isSupersetOf, isDisjointFrom. Lưu ý: argument không nhất thiết là Set thật — nó phải…
structuredClone(value) là hàm built-in deep-clone, dùng thuật toán structured clone của nền tảng (cùng cơ chế postMessage của Web Worker). Hơn JSON.parse(JSON.stringify(x)) ở chỗ: - Giữ được Date, RegExp, Map, Set, ArrayBuffer, TypedArray, Blob, File — JSON làm hỏng (Date thành string, Map thành {}).…
Ba method giúp truy cập từ cuối mảng sạch hơn. - at(index) hỗ trợ chỉ số âm: at(-1) là phần tử cuối. Cách cũ arr[arr.length - 1] dài dòng, và không gọn khi arr là biểu thức. - findLast(fn) / findLastIndex(fn) duyệt từ phải sang…
Thứ tự: sync → nextTick → promise → sau đó timeout và immediate theo thứ tự không cố định. Vì sao: 1. console.log('sync') chạy đồng bộ trước hết. 2. Sau khi stack đồng bộ rỗng, Node xử lý nextTick queue trước, rồi mới tới microtask…
Vì hai hàm nằm ở hai phase khác nhau của event loop, và vị trí bạn gọi chúng quyết định phase nào đến trước. Một vòng lặp đi qua các phase theo thứ tự: timers → pending callbacks → poll → check → close callbacks.…
Trong ngữ cảnh biểu thức (gán vào biến, truyền vào hàm) cả hai đều ra '[object Object]'. Chỉ khi gõ trực tiếp {} + [] ở đầu một dòng lệnh (REPL/console) mới ra 0. Cơ chế: 1. Toán tử + khi có ít nhất một…
Số trong JavaScript là IEEE-754 double (nhị phân 64-bit). Các phân số như 0.1, 0.2 không biểu diễn chính xác được ở hệ nhị phân nên bị làm tròn, cộng vào ra 0.30000000000000004. Với tiền tệ, không dùng float. Ba cách phổ biến: - Lưu…
Key của object thường luôn bị ép về chuỗi (hoặc Symbol). Mọi object dùng làm key đều thành '[object Object]', nên các entry khác nhau ghi đè lẫn nhau. Dùng Map khi: - Key không phải chuỗi (object, số, hàm) — Map so khớp key…
delete gỡ hẳn property khỏi object; gán undefined giữ property lại nhưng đổi giá trị. Khác biệt lộ ra ở mọi thao tác duyệt hoặc kiểm tra sự tồn tại. Hai điểm hay bị hỏi thêm: - delete trả về true cả khi property không…
Không. Object.freeze chỉ đóng băng nông (shallow): nó chặn thêm/xoá/sửa property ở tầng đầu tiên, nhưng object lồng bên trong vẫn sửa được vì frozen chỉ khoá tham chiếu, không khoá thứ mà tham chiếu trỏ tới. Deep freeze phải duyệt đệ quy: Lưu ý…
Vì this trong JavaScript được quyết định lúc gọi hàm, không phải lúc định nghĩa. Khi gán method ra biến, bạn chỉ lấy tham chiếu tới hàm, phần "chủ thể gọi" (obj.) không đi kèm. Gọi fn() không kèm object thì this là undefined ở…
Bug xảy ra khi giá trị hợp lệ lại là falsy. trả về vế phải với mọi falsy: 0, '', false, NaN, null, undefined. Còn ?? chỉ trả vế phải khi vế trái là null hoặc undefined. Trường hợp thực tế hay dính: cờ bật/tắt…
Giá trị mặc định trong destructuring chỉ kích hoạt khi giá trị là undefined, không kích hoạt với null. null là một giá trị hợp lệ nên nó được gán thẳng. Cách xử lý null từ API: destructure rồi lọc riêng, hoặc dùng ?? sau…
Với var in ra 3 3 3. Với let in ra 0 1 2. var có phạm vi function, nên cả ba callback đều capture cùng một biến i. Khi timer chạy (sau khi vòng lặp kết thúc), i đã là 3. let có phạm…
Có hoisting — binding được tạo ngay đầu block — nhưng chưa được khởi tạo. Khoảng từ đầu block tới dòng khai báo gọi là TDZ (Temporal Dead Zone); truy cập trong khoảng này ném ReferenceError. Điểm phân biệt quan trọng: undefined nghĩa là "biến…
Cả hai đều copy nông (shallow): chỉ sao chép các thuộc tính own enumerable ở tầng một. Object lồng bên trong vẫn dùng chung tham chiếu. Đây là nguyên nhân gây bug thường gặp trong React/Redux: tưởng đã tạo state mới nhưng object con vẫn…
Kết quả: 1, 4, 3, 2. - 1 và 4 là code đồng bộ, chạy hết trước mọi thứ khác. - 3 nằm trong microtask queue (callback của Promise). Microtask được chạy hết ngay sau khi call stack rỗng, trước khi event loop nhận task…
Đặc tả quy định async function luôn bọc giá trị trả về vào một Promise. return 1 cho ra Promise đã fulfilled với giá trị 1; throw err cho ra Promise đã rejected chứ không ném đồng bộ. Nếu bên trong return một Promise, kết…
Vì forEach bỏ qua giá trị trả về của callback. Callback async trả về một Promise, forEach vứt đi và chạy tiếp phần tử kế — nên vòng lặp kết thúc ngay lập tức trong khi các save() vẫn đang chạy. Hai cách sửa, chọn…
Vì lúc callback chạy, khối try đã kết thúc từ lâu. try/catch chỉ bảo vệ những gì thực thi trên cùng call stack đồng bộ với nó. setTimeout chỉ đăng ký callback rồi trả về ngay; try thoát, stack rỗng. Sau đó event loop lấy…
Với dashboard, câu trả lời gần như luôn là Promise.allSettled: mỗi widget hiển thị độc lập, một API lỗi không nên xoá trắng cả trang. Chọn theo ngữ nghĩa: - all — fail-fast, reject ngay khi một cái lỗi. Dùng khi thiếu bất kỳ mảnh…
Nguyên tắc: song song mặc định, tuần tự khi có ràng buộc. Bắt buộc tuần tự khi: - Request sau cần kết quả của request trước (lấy token rồi mới gọi API; lấy nextCursor để phân trang). - Thao tác ghi phải đúng thứ tự…
Dùng AbortController — cách duy nhất huỷ được fetch đang chạy. Bản ngắn gọn là AbortSignal.timeout(): Khi cần huỷ thủ công (user rời trang, gõ query mới) thì tự quản controller: Cần vừa timeout vừa huỷ tay thì gộp signal bằng AbortSignal.any([controller.signal, AbortSignal.timeout(5000)]). Các điểm…
Backoff nghĩa là mỗi lần thử lại chờ lâu hơn theo cấp số nhân, cộng thêm jitter ngẫu nhiên để nhiều client không cùng bắn lại một thời điểm. Chỉ retry khi an toàn: - Lỗi mạng, timeout, 429, 503, 502 — tạm thời. -…
Đây là stale response — nhiều request cùng bay, thứ tự phản hồi không đảm bảo theo thứ tự gửi. Request cho "ipho" có thể về sau request cho "iphone" và ghi đè state. Ba cách xử lý, xếp theo mức độ nên dùng: 1.…
Debounce chuẩn chỉ hoãn thời điểm gọi; nó không biết gì về Promise. Ba vấn đề phát sinh khi hàm bên trong là async: 1. Mất giá trị trả về. debounce(fn) trả undefined, nên await debouncedSearch(term) vô nghĩa. Muốn await được thì bản debounce phải…
Về giá trị cuối thì giống nhau — cả hai đều cho ra cùng kết quả cho người gọi. Khác biệt nằm ở try/catch và stack trace. Với return promise, hàm thoát khỏi khối try ngay lập tức rồi mới trả promise ra ngoài; lúc…
Trình duyệt chạy qua 5 bước cố định: 1. Parse HTML → DOM: đọc byte HTML thành cây node. 2. Parse CSS → CSSOM: mọi stylesheet được tải và parse thành cây style. 3. Render tree: ghép DOM + CSSOM, bỏ node không hiển thị…
Khác nhau ở dung lượng, vòng đời, và có tự gửi lên server hay không. - Cookie — ~4KB mỗi cookie, tự động đính kèm mọi request cùng origin (kể cả ảnh, API). Có Expires, và quan trọng là có HttpOnly để JS không đọc…
Một sự kiện đi qua 3 pha: capturing (từ window đi xuống phần tử đích), target (tại chính phần tử), rồi bubbling (nổi ngược lên window). Mặc định addEventListener nghe ở pha bubbling; truyền { capture: true } để nghe ở pha đi xuống. Event…
Ba thuộc tính này chặn ba loại tấn công khác nhau: - HttpOnly — JS (document.cookie) không đọc được cookie. Đây là lớp phòng thủ chính khi trang dính XSS: script chèn vào cũng không lấy được session. - Secure — cookie chỉ gửi qua…
Service worker đi qua: register → install → (waiting) → activate → idle/terminated. - install: chạy một lần cho mỗi phiên bản script, thường dùng để precache asset. - waiting: nếu đã có service worker cũ đang điều khiển trang, bản mới đứng chờ —…
Ba bước: preview ngay, nén trước khi gửi, giải phóng tài nguyên. Preview dùng URL.createObjectURL(file) — nhanh hơn FileReader.readAsDataURL vì không phải mã hoá base64 toàn bộ file vào chuỗi. Bắt buộc revokeObjectURL khi bỏ ảnh hoặc unmount, nếu không blob sẽ nằm lại trong…
Nguyên nhân: trình duyệt vẽ trang bằng theme mặc định trước, mãi tới khi JavaScript của app chạy xong (đọc localStorage, set state) mới đổi sang dark. Khoảng giữa hai mốc đó chính là nhịp nháy trắng. Cách xử lý là quyết định theme trước…
Dùng Intl thay vì tự viết hàm chèn dấu chấm và ghép chuỗi ngày. Những chi tiết hay bị hỏi tiếp: - VND không có phần thập phân — Intl tự biết điều đó, còn hàm tự viết thường in ra ".00" thừa. - Định…
Hai việc: cho người dùng biết và tự phục hồi khi mạng trở lại. Phát hiện. navigator.onLine cùng sự kiện online / offline cho biết máy có kết nối mạng hay không: Lưu ý giới hạn: onLine === true chỉ nghĩa là máy có kết…