MẬT DANH: ZERO — Chương 03: JavaScript (đọc miễn phí) | HÀN NGỮ KOOKMIN
Tủ sách
MẬT DANH: ZERO · Chương 03 · JavaScript
📕 Đặt sách 1 / 1
Đọc miễn phí · Chương 03

Vũ khí thứ ba // 동작

THỔI VÀOSỰ SỐNG
JavaScript

Trang đã đẹp. Nhưng bấm nút — chẳng có gì xảy ra.
세 번째 무기 — 동작

defeatBug() // ⚡ kích hoạt
// một trang web sắp biết... cử động

Chạm nửa phải màn hình để lật trang ›

Phản hồi người dùng · dồn dập

⚠ Phản hồi người dùng — dồn dập "Bấm nút đăng ký mà không thấy gì???"
"Nút hỏng à? Bấm 10 lần rồi."
// một bóng đen lướt qua dòng log... THE BUG đã thức giấc.

Trang thông báo giờ đã tươm tất, có màu, có bố cục. Trưởng nhóm vừa thở phào thì một tin nhắn từ bộ phận chăm sóc khách hàng nhảy lên: hàng loạt người dùng bấm nút "Nhận thông báo khi sửa xong" — nhưng chẳng có gì xảy ra cả. Nút bấm như một cái xác. Và đúng khoảnh khắc đó, một góc màn hình lóe lên dòng chữ đỏ.

Một trang không chuyển động chỉ là một cái xác. Thứ các ngươi tạo ra chỉ là một cỗ quan tài đẹp đẽ. Cái nút kia ư? Nó sẽ không bao giờ hoạt động.

— THE BUG

Tiền bối chỉ mỉm cười. "Trang không hỏng. Nó điếc — em đang nói chuyện với một cái nút không có tai." Anh nhìn ZERO. "Đã đến lúc trao cho em vũ khí cuối của bộ ba nền tảng: thứ gắn trái tim biết đập cho một trang web — JavaScript."

ZERO kích hoạt defeatBug() — lần đầu tiên trang web phản công lại THE BUG

Vũ khí thứ ba kích hoạt — lần đầu tiên, trang web phản công lại THE BUG.

Hai người trước một cái nút "Đăng ký" bất động

ZERO

(bấm nút liên tục) Em bấm gãy chuột rồi mà nó vẫn trơ ra. Nút này hỏng thật à?

Tiền bối

Không hỏng. Nó điếc. Em đang nói chuyện với một cái nút không có tai.

ZERO

…Nút thì làm gì có tai?

Tiền bối

(gõ một dòng) Thì mình lắp cho nó. Đây: "Này nút, khi nào có người bấm vào, hãy làm việc này." Cái "khi nào có người bấm" đó gọi là sự kiện (event).

ZERO

(bấm lại, nút bật ra dòng chữ "Đăng ký thành công") Nó… nghe thấy em rồi!

Khái niệm bước ra từ một câu đùa

Trang web không tự biết bạn vừa bấm, gõ, hay kéo chuột. Bạn phải "lắp tai" cho từng phần tử bằng cách lắng nghe sự kiện. Đó là cách JavaScript biến trang tĩnh thành thứ biết tương tác.

JavaScript là gì?

JavaScript (viết tắt JS) là ngôn ngữ trao cho trang web hành động và phản ứng. Bấm thì menu mở ra, gõ thì kiểm tra dữ liệu, hết giờ thì màn hình đổi — mọi "chuyển động" đều là tác phẩm của JS.

Bộ ba hoàn chỉnh

HTML = khung xương (cơ thể) · CSS = vẻ ngoài (quần áo) · JavaScript = hành vi (cơ bắp & thần kinh). Đủ ba thứ, web mới thật sự sống. Bạn vừa nắm bộ ba quyền năng của web.

Ba lớp của một trang web: HTML dựng khung xương, CSS khoác áo màu sắc, JavaScript thổi vào sự sống

Biến — chiếc hộp chứa thông tin

Biến — Variable · 변수

Là một chiếc hộp có dán nhãn (tên) để chứa một giá trị: một con số, một đoạn chữ… Bạn đặt tên cho hộp, rồi gọi tên đó để lấy lại giá trị bất cứ lúc nào.

script.js
let codeName = "ZERO"; // hộp tên codeName chứa chữ "ZERO"
let hoursLeft = 72; // hộp tên hoursLeft chứa số 72

Hàm — bó các mệnh lệnh

Hàm — Function · 함수

Là gói nhiều mệnh lệnh thành một và đặt cho nó một cái tên. Tạo một lần, sau đó chỉ cần gọi tên là cả gói lệnh chạy lại — khỏi viết lại từ đầu.

script.js — hàm chặn bug
function defeatBug() {
  alert("Đã cách ly con bug!"); // hiện hộp thông báo
}

<!-- Gắn hàm vào nút: bấm là chạy -->
<button onclick="defeatBug()">Cách ly</button>

Giờ nút bấm đã sống. Vừa bấm, hàm liền chạy, một hộp thông báo bật lên. Trang web đã bắt đầu phản ứng với người dùng.

Câu điều kiện (if) — để máy biết phán đoán

Câu điều kiện — if · 조건문

Cho phép chương trình hành động khác nhau tùy tình huống. "Nếu (if) điều này đúng thì làm A, nếu không (else) thì làm B". Đây là cách trao khả năng phán đoán cho máy.

script.js
if (hoursLeft < 24) {
  alert("Báo động! Còn chưa đầy 24 giờ!");
} else {
  alert("Vẫn còn thời gian.");
}

Vòng lặp — làm việc lặp lại mà không mỏi tay

Giả sử bạn muốn in ra "Cố lên!" mười lần. Chẳng lẽ gõ mười dòng? Vòng lặp giúp máy lặp lại một việc bao nhiêu lần tùy ý.

Vòng lặp — Loop · 반복문

Cấu trúc giúp lặp lại một đoạn lệnh nhiều lần. Phổ biến nhất là vòng for: "làm việc này, lặp lại đúng N lần."

loop.js
// Đếm từ 1 đến 5, in mỗi số ra
for (let i = 1; i <= 5; i++) {
  console.log("Lần thứ " + i);
}
// Kết quả: Lần thứ 1, 2, 3, 4, 5
💡 Đọc vòng for như một câu

Ba phần trong ngoặc: bắt đầu từ đâu (let i=1), lặp đến khi nào (i<=5), mỗi vòng thay đổi ra sao (i++ là tăng i thêm 1). Đừng học thuộc — hãy đọc: "bắt đầu từ 1, chừng nào còn ≤ 5 thì làm, mỗi lần tăng 1."

Sức mạnh thật sự của JS

Thay đổi nội dung ngay khi người dùng bấm

Sức mạnh thật sự của JS là thay đổi trang ngay khi người dùng thao tác. Ví dụ kinh điển: bấm nút thì đổi chữ.

tuongtac.html
<h1 id="loitho">Xin chào</h1>
<button onclick="doiChu()">Bấm tôi</button>

<script>
function doiChu() {
  // tìm phần tử id "loitho", đổi nội dung
  document.getElementById("loitho").innerText = "Bạn vừa dùng JavaScript!";
}
</script>
🔎 Nền tảng của mọi trang tương tác

document.getElementById("…") là cách JS "tìm" một phần tử trên trang qua id của nó. Tìm được rồi, bạn đổi innerText (chữ bên trong) là nội dung thay đổi ngay trước mắt người dùng.

✓ Kiểm tra nhanh

1. Muốn lặp một việc 10 lần, dùng gì?
Vòng lặp for: for (let i=1; i<=10; i++) { … }

2. Làm sao để JS tìm một phần tử trên trang?
document.getElementById("tên-id") — tìm theo id đã đặt trong HTML.

3. "let codeName = 'ZERO'" nghĩa là gì?
Tạo một biến tên codeName, chứa đoạn chữ "ZERO".

⚿ Không có trên Google · ghi chép từ người đi trước

5 lỗi JavaScript khiến fresher bị "bóc" ngay khi review

✕ Không hiểu == khác === — dùng nhầm gây bug ẩn rất khó tìm.

✕ Sửa trực tiếp dữ liệu gốc thay vì tạo bản sao — làm hỏng state của cả ứng dụng.

✕ Quên rằng map/filter trả về mảng mới, vẫn cố sửa mảng cũ.

✕ Đặt tên biến a, data, temp — người review không hiểu bạn định làm gì.

✕ Không xử lý dữ liệu rỗng (null/undefined) — app crash ngay khi gặp ca lạ.

Vì sao quan trọng: JavaScript là nơi logic sống. Một dòng JS cẩu thả không chỉ xấu — nó làm sập tính năng thật mà khách đang dùng.

# nox-dev
Trưởng nhóm · 10:12

Nút "Thêm vào giỏ" bấm không ăn trên production. Ai vừa đẩy code lên thế?

ZERO · 10:15

Để em xem console… Có lỗi Cannot read property of undefined anh ạ. Em quên check mảng rỗng.

Senior · 10:18

Đúng rồi. Luôn hỏi "nếu dữ liệu trống thì sao?" trước khi đẩy code. Thêm điều kiện rồi test lại nhé.

✦ Trợ thủ AI · dùng đúng cách

Sau khi học JS, dùng AI để hiểu sâu — đừng chỉ copy code

💬 "Giải thích đoạn JavaScript này từng dòng, và chỉ ra nó sẽ lỗi trong trường hợp dữ liệu nào."

💬 "Viết lại đoạn vòng lặp này bằng map/filter cho gọn, giải thích vì sao cách đó tốt hơn."

Claude · ChatGPT · Cursor · Copilot — công cụ chỉ mạnh khi bạn đủ hiểu để đặt đúng câu hỏi.

✎ Nhật ký của ZERO — đêm thứ ba

"Hôm nay cái nút của mình… đã nghe thấy mình. Mình bấm, nó trả lời. Nghe nhỏ bé thôi, nhưng mình ngồi lặng đi mất một lúc. Ba đêm trước mình còn không biết một trang web được làm từ gì. Giờ mình vừa làm một thứ biết phản ứng với con người.

Nhưng tối nay tiền bối có gì đó rất lạ. Khi con bug bị chặn, lẽ ra anh phải vui. Vậy mà anh nhìn màn hình như đang nhìn một bóng ma."

Được giao đọc một đoạn code thật của đội, ZERO mở file ra — và khựng lại. Đầy những dấu ngoặc vuông, dấu ngoặc nhọn, dấu chấm nối tiếp nhau: users[0].name, danhSach.map(…). Cô biết từng chữ cái, nhưng ghép lại thì mù tịt. "Sao code thật trông khác xa những gì em học thế này?"

Tiền bối

"Vì em mới học bảng chữ cái — biến, hàm, điều kiện. Giờ là lúc ghép chữ thành câu. Ba thứ này: mảng, đối tượng, sự kiện — xuất hiện trong gần như mọi dòng JavaScript thật. Nắm được chúng, em đọc được code của bất kỳ ai. Kể cả code của kẻ đang tấn công ta."

Mảng — một danh sách trong một biến

Mảng — Array · 배열

Là một biến chứa nhiều giá trị theo thứ tự, thay vì chỉ một. Như một hộp có nhiều ngăn được đánh số. Dùng để lưu danh sách: tên người dùng, sản phẩm, công việc…

array.js
// Một mảng chứa danh sách việc cần làm
let vieccanlam = ["Học HTML", "Học CSS", "Học JS"];

console.log(vieccanlam[0]); // "Học HTML" (đếm từ 0!)
vieccanlam.push("Làm dự án"); // thêm vào cuối mảng
🔑 Mảng đếm từ 0, không phải 1!

Phần tử đầu tiên là vị trí [0], thứ hai là [1]… Điều này gây bối rối cho mọi người mới, nhưng quen rất nhanh. push thêm phần tử vào cuối, length cho biết mảng có bao nhiêu phần tử.

Sự kiện — phản ứng với người dùng

Sự kiện — Event · 이벤트

Là những "việc xảy ra" mà người dùng tạo ra: bấm chuột (click), gõ phím (keypress), di chuột (mouseover)… JavaScript "lắng nghe" các sự kiện này và phản ứng lại — đây là linh hồn của trang web tương tác.

event.js
// Lắng nghe sự kiện "click" trên nút
let nut = document.getElementById("nutBam");

nut.addEventListener("click", function() {
  alert("Bạn vừa bấm nút!");
});
🎧 addEventListener — "đeo tai nghe" cho phần tử

Bảo một phần tử: "khi có sự kiện X thì làm Y". Đây là cách chuyên nghiệp hơn so với onclick viết thẳng trong HTML, và là thứ bạn sẽ gặp trong mọi dự án thật.

✓ Kiểm tra nhanh

1. Phần tử đầu tiên của mảng nằm ở vị trí số mấy?
Vị trí [0] — mảng đếm từ 0.

2. Object dùng để làm gì?
Gom nhiều thông tin liên quan về một thứ vào một chỗ, dạng "tên: giá trị".

3. addEventListener("click", …) làm gì?
Lắng nghe sự kiện bấm chuột trên phần tử, và chạy hàm khi sự kiện xảy ra.

Mục tiêu: app To-do hoạt động được

"Hello World" của ứng dụng tương tác

Một ô nhập việc, một nút thêm, và danh sách việc hiện ra bên dưới. Bấm vào việc để đánh dấu hoàn thành. Đây là app tương tác đầu tiên của bạn — ghép đủ cả mảng, sự kiện và DOM.

Bước 1 — Khung HTML

todo.html
<h1>Việc cần làm</h1>
<input id="oNhap" placeholder="Nhập việc...">
<button onclick="themViec()">Thêm</button>
<ul id="danhSach"></ul>

Bước 2 — Logic JavaScript

todo.js
function themViec() {
  // lấy chữ người dùng gõ vào ô
  let o = document.getElementById("oNhap");
  let viec = o.value;

  // tạo một mục <li> mới
  let li = document.createElement("li");
  li.innerText = viec;

  // bấm vào việc thì gạch ngang (đánh dấu xong)
  li.addEventListener("click", function() {
    li.style.textDecoration = "line-through";
  });

  // thêm vào danh sách, xóa ô nhập
  document.getElementById("danhSach").appendChild(li);
  o.value = "";
}
◎ Bạn vừa làm một app thật!

Nó dùng: lấy giá trị từ ô nhập (o.value), tạo phần tử mới (createElement), lắng nghe sự kiện (addEventListener), và thêm vào trang (appendChild). Đây chính là cách mọi ứng dụng web tương tác hoạt động — chỉ phức tạp hơn về quy mô.

▼ Kết quả: app To-do đang chạy
Nhập việc…
Thêm
Học JavaScript
Làm trang giới thiệu ✓
Làm app To-do

Nâng cấp khi đã quen

Nâng cấpGợi ý
Nút xóa cho mỗi việcThêm một nút nhỏ, dùng li.remove()
Đếm số việc còn lạiDùng một biến, cập nhật mỗi khi thêm/xong
Lưu lại khi tải lại trangHọc về localStorage (bước tiếp theo)
Tô màu theo độ ưu tiênThêm class CSS tùy mức quan trọng
▣ Nhiệm vụ lớn 2 — App To-do của riêng bạn

☐ Gõ lại đầy đủ HTML + JS ở trên thành app chạy được
☐ Thử thêm vài việc, bấm để đánh dấu xong
☐ Chọn ít nhất 1 nâng cấp để làm thêm (gợi ý: nút xóa)
☐ Đẩy lên GitHub bằng những gì sẽ học ở chương Git

ZERO bấm "Thêm", và việc đầu tiên hiện ra. Cô thêm việc thứ hai, thứ ba. Bấm vào một việc — nó gạch ngang. Cô ngả người ra ghế, nhìn thứ mình vừa tạo ra: không phải một bài tập, mà một công cụ thật sự dùng được. "Em làm được một cái app rồi," cô thì thầm. Tiền bối chỉ cười: "Và đó mới chỉ là cái đầu tiên thôi."

🛠 Phần thực hành — tùy chọn

Mục này hướng dẫn tự tay gõ code. Nếu bạn theo hướng cầu nối, phiên dịch hay sales IT và chưa cần code thật, có thể đọc lướt để nắm ý rồi đi tiếp — không ảnh hưởng mạch truyện. Khi nào muốn thử, hãy quay lại đây.

Sổ tay // làm app biết nhớ

— App To-do của bạn quên hết khi tải lại trang. Hãy dạy nó cách nhớ. —

ZERO khoe app To-do với tiền bối, mặt rạng rỡ. Anh gật gù, rồi nhấn F5 tải lại trang — và mọi việc cô vừa thêm biến mất sạch. Mặt cô xịu xuống. "Đừng buồn," anh cười. "Đó là vì app chưa biết 'nhớ'. Hôm nay anh dạy em cho nó một bộ não."

Vì sao app "quên" khi tải lại?

Mọi thứ trong JavaScript sống trong bộ nhớ tạm — khi tải lại trang, bộ nhớ đó xóa sạch để bắt đầu mới. Để dữ liệu "sống sót" qua mỗi lần tải, ta cần lưu nó vào một nơi bền hơn. Cách đơn giản nhất cho người mới: localStorage.

localStorage · 로컬 스토리지

Là một "ngăn tủ" nhỏ ngay trong trình duyệt, cho phép lưu dữ liệu dạng chữ tồn tại lâu dài — kể cả khi đóng trình duyệt rồi mở lại. Lý tưởng để lưu những thứ nhỏ: cài đặt, danh sách việc, lựa chọn của người dùng.

Chiếc hộp trí nhớ — localStorage giúp app không quên khi tải lại trang

ZERO cất từng việc vào "chiếc hộp trí nhớ" của trình duyệt. Nhấn F5, tắt mở lại — danh sách vẫn còn nguyên. App của cô vừa biết nhớ.

storage.js
// Lưu một giá trị
localStorage.setItem("tenNguoiDung", "ZERO");

// Lấy lại giá trị (kể cả sau khi tải lại trang)
let ten = localStorage.getItem("tenNguoiDung");
console.log(ten); // "ZERO" — vẫn còn!

// Xóa một giá trị
localStorage.removeItem("tenNguoiDung");
⫶ Ba lệnh là đủ

setItem (lưu), getItem (lấy), removeItem (xóa). localStorage chỉ lưu được chữ (text), nên khi lưu mảng/object phải đổi sang chữ bằng JSON.stringify(), và đổi ngược lại bằng JSON.parse() khi lấy ra.

Áp dụng: làm To-do biết nhớ

todo-luu.js
// Lưu cả mảng việc (đổi sang chữ trước)
let danhSachViec = ["Học JS", "Làm app"];
localStorage.setItem("viec", JSON.stringify(danhSachViec));

// Khi tải lại trang, lấy ra và đổi về mảng
let luu = localStorage.getItem("viec");
if (luu) {
  danhSachViec = JSON.parse(luu); // việc cũ vẫn còn!
}
⚠ Lưu ý quan trọng

localStorage chỉ lưu trên chính máy/trình duyệt đó — không đồng bộ giữa các thiết bị. Để dữ liệu đi theo người dùng khắp nơi, cần lưu trên server/database. localStorage tuyệt cho việc nhỏ, nhưng không thay được database thật.

▣ Nhiệm vụ storage — dạy app To-do cách nhớ

☐ Thêm localStorage vào app: mỗi khi thêm việc thì lưu lại
☐ Khi tải trang, đọc lại danh sách đã lưu và hiển thị
☐ Thử thêm vài việc, nhấn F5 — chúng vẫn còn đó!
☐ Cảm nhận khoảnh khắc app của bạn "biết nhớ" lần đầu

ZERO thêm vài dòng code, rồi hồi hộp nhấn F5. Trang tải lại… và danh sách việc vẫn còn nguyên. Cô bật cười thành tiếng. Một thứ nhỏ bé thôi, nhưng với cô nó như phép màu: app của cô vừa có được trí nhớ đầu tiên.

▶ 02:00 sáng · ZERO quay màn hình về phía tiền bối

ZERO

Anh nhìn đi. Nó viết lại app của em trong chín giây. AI giờ code được hết rồi… (giọng khẽ lại) vậy em học mấy thứ này làm gì nữa?

Tiền bối

(không nhìn cái app, mà nhìn thẳng vào cô) Cho anh hỏi một câu. Khách hàng nói: "Làm cho tôi cái gì đó giống Instagram, nhưng dành cho người yêu chó, mà phải sang, đừng rối, kiểu trẻ trung nhưng đừng trẻ con." — Em bảo AI viết đi. Nó viết được không?

ZERO

(ngập ngừng) …Nó sẽ hỏi lại "sang" là sang thế nào.

Tiền bối

Không. Nó đoán. Và nó đoán theo trung bình của cả triệu trang web nó từng thấy. AI viết được dòng code. Nhưng nó không biết nên viết dòng nào. Nó không ngồi trong phòng họp khi khách đổi ý lần thứ tư. Nó không hiểu vì sao một cái nút phải nằm ở góc đó chứ không phải góc kia. Yêu cầu của con người luôn mơ hồ — và biến cái mơ hồ thành cái rõ ràng, đó là phần AI không làm được.

Tiền bối

"A." có AI viết code nhanh như chớp. Nhưng hắn vẫn cần một thứ: một con người hiểu hệ thống đủ sâu để biết tấn công vào đâu. Đó là lý do hắn từng là người trong đội. Và cũng là lý do em phải học — không phải để gõ nhanh hơn máy, mà để hiểu thứ máy không hiểu.

ZERO nhìn lại cái app "hoàn hảo" của "A." — và lần đầu cô thấy nó thiếu một thứ: nó không biết vì sao nó tồn tại. Code thì máy viết được. Nhưng hiểu con người cần gì — đó vẫn là việc của con người.

Còn nội dung — cuộn xuống