我的网页
欢迎光临
- 项目1
- 项目2
Mở đầu
Đã học HTML + CSS, làm web đẹp được. Nhưng click button không phản ứng, submit form không gửi, web như "static image".
Đây là lý do cần JS — làm web "sống dậy". Click button hiện menu, gõ text search realtime, scroll load thêm content...
Trong vibecoding, AI viết đa phần code. Nhưng bạn phải đọc được code làm gì, không AI sai cũng không biết. Đọc xong:
Bạn sẽ học:
| Chương | Nội dung |
|---|---|
| 1 | JS là gì |
| 2 | Data + variable |
| 3 | Function + logic |
| 4 | DOM + event |
| 5 | Thực chiến |
| Không có JS | Có JS |
|---|---|
| Content cố định, không interact | Click button hiện menu |
| Click button không phản ứng | Gõ text search realtime |
| Submit form không gửi | Scroll auto load |
| Page không auto update | Data realtime |
| Tech | Ẩn dụ | Use |
|---|---|---|
| HTML | Khung xương | Define structure + content |
| CSS | Da | Define appearance + style |
| JS | Cơ + thần kinh | Cho web respond + interact + think |
Pit dev mới
1 dev JS mới dùng AI làm "counter" — click button, số +1. AI gen code chạy được.
Muốn đổi thành "+2 mỗi click", bảo AI: "mỗi click +2". AI sửa, nhưng số vẫn chỉ +1.
Hỏi AI sao không hiệu quả, AI giải thích, nhưng dev không hiểu count = count + 1 nghĩa gì, không biết AI sửa chỗ đó không. Cứ "thêm 2 không hiệu quả", AI sửa nhiều version: có version đổi init thành 2, có version add 2 ở chỗ không liên quan.
Cuối cùng đọc chương 2 "variable", hiểu count = count + 1 = gán count thêm 1 rồi lưu lại. Bảo AI: "đổi count + 1 thành count + 2".
1 lần là OK.
Đó là sao cần hiểu JS — không phải viết code, mà khi AI sai, bạn thấy đúng vấn đề, 1 câu nói trúng.
const colors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4']
let currentIndex = 0
const button = document.querySelector('#changeBtn')
button.addEventListener('click', () => {
currentIndex = (currentIndex + 1) % colors.length
document.body.style.backgroundColor = colors[currentIndex]
})| Code | Use | Chương |
|---|---|---|
const colors = [...] | Define color data | 2: Array |
let currentIndex = 0 | Record color hiện tại | 2: Variable |
document.querySelector(...) | Tìm button | 4: DOM |
button.addEventListener(...) | Click event | 4: Event |
() => {...} | Code chạy khi click | 3: Arrow function |
Insight
JS code = chuỗi instruction bảo browser "khi user làm X, phải xảy ra gì"
Variable như box có label — bỏ data vào, lấy bằng label.
const name = "Hoàng" // Tên không đổi → const
let age = 25 // Tuổi có thể đổi → let| Keyword | Đổi được? | Use | Vd |
|---|---|---|---|
const | Value không đổi | CCCD, config, color list | |
let | Value đổi | Counter, selection, input |
const PI = 3.14159
const MAX_USERS = 100
let count = 0
count = 1 // ✅ OK
count = count + 1 // ✅ OK
const fixedCount = 0
fixedCount = 1 // ❌ Error! const không reassignconst name = "张三"let age = 25| Type | Note | Vd | Use |
|---|---|---|---|
string | Text | "hello" | Username, description |
number | Số | 42, 3.14 | Price, quantity |
boolean | Y/N | true, false | Logged-in, completed |
Special:
undefined → declared chưa valuenull → cố ý emptyconst name = "Hoàng"
const age = 25
// Traditional (phiền)
const message = "Tên " + name + ", tuổi " + age
// Template string (gọn)
const message = `Tên ${name}, tuổi ${age}`Thấy backtick + ${} → đang chèn variable vào text.
Object = nhóm property có tên (như info card):
const user = {
name: "Hoàng",
age: 25,
isVIP: true
}
console.log(user.name) // "Hoàng"
console.log(user.age) // 25Array = list có order:
const colors = ['red', 'green', 'blue']
console.log(colors[0]) // "red"
console.log(colors[1]) // "green"Nested (hay nhất trong AI code):
const todos = [
{ id: 1, text: "Học JS", done: false },
{ id: 2, text: "Làm project", done: true }
]
console.log(todos[0].text) // "Học JS"Nhận diện
{} → object[] → arraydata[0].name → lấy item 0, rồi prop namePrimitive (string, number, boolean) = copy value:
let a = 10
let b = a
b = 20
console.log(a) // 10 (a không đổi)Object/Array = copy address (cùng trỏ về 1 thứ):
let user1 = { name: "Hoàng" }
let user2 = user1
user2.name = "Linh"
console.log(user1.name) // "Linh" (user1 cũng đổi!)Sao cần copy? Trong React/Vue, sửa data trực tiếp UI không update. AI code hay có [...array] hoặc {...obj} — tạo copy:
const arr1 = [1, 2, 3]
const arr2 = [...arr1]
arr2.push(4)
console.log(arr1) // [1, 2, 3] (không đổi)
console.log(arr2) // [1, 2, 3, 4]let a = 10
let b = a // b=10
b = 20 // a还是10let obj1 = {age:25}
let obj2 = obj1
obj2.age=30 // obj1也变了!
// 用 {...obj1} 复制Destructuring: lấy data từ object/array nhanh
const user = { name: "Hoàng", age: 25, city: "HCM" }
// Traditional
const name = user.name
const age = user.age
// Destructuring
const { name, age } = userSpread: copy + expand
// Array
const arr1 = [1, 2, 3]
const arr2 = [...arr1, 4, 5] // [1, 2, 3, 4, 5]
// Object
const user1 = { name: "Hoàng", age: 25 }
const user2 = { ...user1, city: "HCM" }if/else:
const age = 18
if (age >= 18) {
console.log("Adult")
} else {
console.log("Minor")
}Ternary (if/else 1 dòng):
const message = age >= 18 ? "Adult" : "Minor"&& short-circuit (React hay dùng):
isLoggedIn && <UserPanel /> // Chỉ hiện khi logged inFunction = công thức món ăn:
function greet(name) {
return "Hello " + name
}
console.log(greet("Hoàng")) // "Hello Hoàng"3 cách viết:
// 1. function declaration
function greet(name) { return "Hello " + name }
// 2. Arrow function (AI hay dùng nhất)
const greet = (name) => { return "Hello " + name }
// 3. Arrow function ngắn (1 dòng)
const greet = (name) => "Hello " + nameprice * discountconst calculatePrice = (price, discount) => {
return price * discount
}
// 或者更简洁:
const calculatePrice = (price, discount) => price * discountReact/Vue list render hay dùng:
const todos = [
{ id: 1, text: "Học", done: false },
{ id: 2, text: "Làm", done: true }
]
// .map(): biến mỗi item thành cái khác
const texts = todos.map(todo => todo.text)
// ["Học", "Làm"]
// .filter(): lọc
const unfinished = todos.filter(todo => !todo.done)
// .find(): tìm cái đầu match
const found = todos.find(todo => todo.id === 1)Ẩn dụ "phòng":
const global = "Hành lang"
function room() {
const local = "Trong phòng"
console.log(global) // ✅ Thấy hành lang
}
console.log(local) // ❌ Ngoài không thấy đồ trong phòngconst appName = "Todo" // 全局作用域
function greet() {
const message = "你好" // 函数作用域
if (true) {
const greeting = message + appName // 块级作用域
console.log(greeting)
}
console.log(greeting) // ❌ 报错!外层看不到内层
}function setupCounter() {
let count = 0
return {
add: () => { count++; return count },
getCount: () => count
}
}
const counter = setupCounter()
counter.add() // 1
counter.add() // 2
counter.getCount() // 2Core: function nhớ variable xung quanh lúc tạo, dù outer function đã chạy xong.
this: ai gọi function Scenario 1: trong method object, this trỏ object:
const user = {
name: "Hoàng",
sayHi() {
console.log("Hi, " + this.name) // this = user
}
}Scenario 2: trong event listener, this trỏ element trigger:
button.addEventListener('click', function() {
console.log(this) // this = button
})
// Arrow function không đổi this
button.addEventListener('click', () => {
console.log(this) // this = outer this
})Bug
AI code có bug this (vd Cannot read property of undefined) → bảo AI: "method này this sai, đổi arrow function hoặc dùng bind"
Web trong JS = "tree", mỗi HTML tag = node.
<html>
<body>
<h1>Title</h1>
<p>Paragraph</p>
</body>
</html>JS op web = find node + modify node + create/delete node
欢迎光临
点击上方按钮查看对应代码// Find (CSS selector, hay dùng)
const title = document.querySelector('h1')
const button = document.querySelector('#btn')
const items = document.querySelectorAll('.item')
// Modify
title.textContent = "Title mới"
element.style.color = "red"
element.classList.add('active')
element.classList.remove('hidden')
element.classList.toggle('open')button.addEventListener('click', () => {
console.log("Clicked")
})| Event | Trigger | Use |
|---|---|---|
click | Click | Button, link |
input | Input change | Realtime search |
submit | Form submit | Login, register |
scroll | Scroll | Lazy load |
input.addEventListener('input', (e) => {
console.log(e.target.value)
e.preventDefault() // Chặn default behavior
})Ẩn dụ nhà hàng: order xong không đứng ở cửa bếp đợi, làm việc khác, món xong phục vụ mang ra.
// Sync (block page, đừng dùng)
const data = fetch('/api/data') // ❌ block
// Async (đúng)
async function loadData() {
try {
const response = await fetch('/api/data')
const data = await response.json()
console.log(data)
} catch (error) {
console.error('Lỗi:', error)
}
}async → mark function có asyncawait → đợi op xong (không block)try/catch → handle errorconsole.log("1")
console.log("2") // 等上面执行完
console.log("3")
// 输出:1, 2, 3console.log("1")
setTimeout(() => console.log("2"), 1000)
console.log("3")
// 输出:1, 3, 2JS = "1 person workstation", 1 lúc 1 việc, có "todo sticky notes" (task queue).
Gặp op đợi (network, timer), JS không đợi ngu, dán "xong làm gì" lên notes, mình tiếp tục. Xong việc hiện tại, mới xem notes.
console.log("1")
setTimeout(() => console.log("2"), 0)
console.log("3")
// Output: 1, 3, 2 (không phải 1, 2, 3!)Vì sao?
console.log("1") → 1setTimeout → dán callback lên notes, tiếp tụcconsole.log("3") → 3setTimeout → 2执行顺序:还未开始
代码书写顺序:1, 2, 3, 4, 5
代码从上到下写的,但执行顺序不一定从上到下——因为异步操作会被"推迟"到当前代码执行完之后。
AI gen React/Vue code line đầu hầu hết là import.
import = lấy function từ file khác:
import { formatDate } from './utils'
import React from 'react'
import { useState } from 'react'export = expose function:
// utils.js
export function formatDate(date) { ... }
// Hoặc default
export default function formatDate(date) { ... }npm package = tool người khác viết, install xong dùng:
// npm install lodash
import _ from 'lodash'4 step:
| Step | Xem | Vd |
|---|---|---|
| 1. Cấu trúc | Có mấy function? Làm gì? | loadData() load, renderList() render |
| 2. Tìm entry | Code start từ đâu? | addEventListener('click', ...) |
| 3. Theo data flow | Data từ đâu, đi đâu? | API → parse → render |
| 4. Detail logic | Function cụ thể xử thế nào? | Loop, judge, calc |
| Error | Nghĩa | Bảo AI |
|---|---|---|
TypeError: Cannot read properties of undefined | Truy cập prop của thứ không tồn tại | "Line X báo, biến undefined, check gán" |
ReferenceError: xxx is not defined | Variable chưa declare | "Variable xxx chưa define, typo hoặc quên import?" |
TypeError: xxx is not a function | Call cái không phải function | "xxx không phải function, check type + source" |
SyntaxError: Unexpected token | Syntax sai | "Line X syntax error, check ngoặc + punctuation" |
CORS error | Browser chặn cross-origin | "CORS error, cần config CORS" |
404 Not Found | Resource không tồn tại | "API trả 404, check URL" |
Khoảng cách newbie vs pro: độ chính xác mô tả vấn đề.
| Tệ | Tốt |
|---|---|
| "Code có bug" | "Click delete button, xoá item cuối thay vì current" |
| "Style sai" | "Title phải center, hiện đang left" |
| "Data không hiện" | "fetch trả data (console thấy), nhưng page không re-render" |
| "Add feature" | "Trong user list page add search box, gõ filter realtime theo name fuzzy" |
| "Click không phản ứng" | "Click button console báo 'Cannot read property of undefined' line X" |
const/let → biết có reassign được không{} → object / [] → array{...obj} / [...arr] → tạo copyfunction / => → define opif/else / ? : → judge.map() / .filter() → transform/filter arraydocument.querySelector → tìm elementaddEventListener → listen user actionasync/await → đợi op tốn timeimport/export → import/export moduleConcept core:
this bản chất: tuỳ ai gọi functionBảo AI
2026 cho VN dev
Object.groupBy, Promise.withResolvers, top-level await