- Hiểu lịch sử JavaScript ngắn gọn: từ Netscape 1995 đến ECMAScript 2024.
- Phân biệt JavaScript ngôn ngữ (spec) — runtime (V8, SpiderMonkey, JSC) — môi trường (browser, Node, Deno, Bun).
- Chạy được JS ở 3 nơi: browser DevTools console, Node REPL, file
.jsquanode. - Biết tooling baseline:
node,npm/pnpm,npx, ESLint, Prettier. - Setup được project mini:
npm init,package.json,node_modules. - Hiểu khái niệm "ESM vs CommonJS" ở mức intro — chi tiết để chương 8 đào sâu.
1. JavaScript là gì? Lịch sử ngắn
JavaScript được tạo ra trong 10 ngày bởi Brendan Eich tại Netscape, năm 1995. Mục tiêu: thêm "scripting nhẹ" cho trình duyệt Netscape Navigator — một thứ ngôn ngữ mà designer hoặc người không-phải-kỹ sư có thể viết được. Tên ban đầu là Mocha, sau đổi LiveScript, cuối cùng đổi JavaScript vào tháng 12 năm 1995 — đơn thuần là marketing khi Java đang là ngôn ngữ hot.
JavaScript và Java không liên quan gì nhau về thiết kế. Java là static typed, class-based, compile sang bytecode. JS là dynamic, prototype-based, interpreted. Tên giống nhau chỉ là chiêu marketing 30 năm trước — nhưng nó gây nhầm lẫn đến tận hôm nay.
Năm 1996, Netscape submit JavaScript cho ECMA International để chuẩn hoá. Spec ra đời với tên ECMAScript (ES). Lịch sử các phiên bản:
| Phiên bản | Năm | Highlight |
|---|---|---|
| ES1, ES2, ES3 | 1997-1999 | Spec đầu tiên. Regex, try/catch (ES3). |
| ES4 | — | Bị bỏ. Quá tham vọng, không consensus. |
| ES5 | 2009 | strict mode, getter/setter, JSON.parse/stringify. |
| ES6 / ES2015 | 2015 | Cách mạng: let/const, arrow function, class, module (import/export), Promise, destructuring, template literal, Map/Set, generator. |
| ES2016 | 2016 | ** operator, Array.prototype.includes. |
| ES2017 | 2017 | async/await, Object.entries/values. |
| ES2018-2019 | 2018-2019 | Rest/spread for object, flat/flatMap, optional catch binding. |
| ES2020 | 2020 | Optional chaining ?., nullish coalescing ??, BigInt, dynamic import. |
| ES2021 | 2021 | Logical assignment ??=, Promise.any, numeric separator 1_000_000. |
| ES2022 | 2022 | Top-level await, private field #x, .at() method. |
| ES2023-2024 | 2023-2024 | Non-mutating array methods (toSorted, toReversed), well-formed Unicode strings, decorators stage 3. |
Quy trình chuẩn hoá quản lý bởi TC39 (Technical Committee 39) — gồm đại diện từ các công ty implement engine (Google, Mozilla, Apple, Microsoft, Meta...). Mỗi proposal đi qua 4 stage (0 → 4). Đến stage 4 mới vào spec chính thức. Mỗi tháng 6, spec năm đó được finalize.
Bạn không cần thuộc lịch sử ES. Chỉ cần nhớ: ES6 (2015) là cột mốc lớn — sau đó JS thành ngôn ngữ "đáng dùng". Mọi code ví dụ trong sub-pillar này dùng ES2020+ như mặc định.
2. Ngôn ngữ vs Runtime vs Môi trường — tách 3 lớp
Đây là phần mà phần lớn người mới JS bỏ qua, và sau đó gặp confusion. Hãy phân biệt rõ:
Lớp 1 — JavaScript ngôn ngữ (ECMAScript spec)
Là tập hợp các quy tắc về cú pháp + semantic + standard library tối thiểu (Math, Array, String, Promise...). Spec là tài liệu PDF, không phải code. Mọi engine phải implement đúng spec.
Lớp 2 — Engine (runtime nhỏ)
Là implementation cụ thể của spec. Vài engine phổ biến: V8 (Google, dùng cho Chrome + Node), SpiderMonkey (Mozilla, dùng cho Firefox), JavaScriptCore (Apple, dùng cho Safari + Bun), Hermes (Meta, dùng cho React Native).
Lớp 3 — Host environment (môi trường + API)
Là "bộ wrap" quanh engine, cung cấp API ngoài spec. Browser cung cấp DOM, fetch, localStorage,
setTimeout. Node cung cấp fs, http, process. Cùng V8
engine nhưng API khác nhau.
Ngôn ngữ giống "luật giao thông". Engine giống "xe". Host environment giống "đường phố Hà Nội vs đường cao tốc". Cùng xe (V8), cùng luật (ES spec), nhưng đường khác (browser vs Node) → trải nghiệm lái khác.
Bảng so sánh API có sẵn ở từng môi trường:
| API | Browser | Node | Deno | Bun |
|---|---|---|---|---|
console.log | ✅ | ✅ | ✅ | ✅ |
setTimeout | ✅ | ✅ | ✅ | ✅ |
fetch | ✅ | ✅ (v18+) | ✅ | ✅ |
document (DOM) | ✅ | ❌ | ❌ | ❌ |
fs.readFile | ❌ | ✅ | ✅ (Deno.readFile) | ✅ |
process.env | ❌ | ✅ | ❌ (Deno.env) | ✅ |
localStorage | ✅ | ❌ | ✅ | ❌ |
Khi bạn copy code từ Stack Overflow, luôn tự hỏi: code này chạy ở đâu? Nếu nó dùng
document.getElementById → browser only. Nếu nó dùng require('fs') → Node only.
Không có nghĩa "JavaScript" universal.
3. Chạy JS trong browser — DevTools Console
Mở Chrome/Firefox/Safari. Bấm F12 (Windows) hoặc Cmd + Option + I (Mac). Tab Console là REPL (Read-Eval-Print Loop) của JS — gõ expression, Enter, xem kết quả.
Thử ngay:
> 1 + 1
2
> document.title
'Google' // hoặc title của trang đang mở
> navigator.userAgent
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...'
> [1, 2, 3].map(x => x * 2)
[2, 4, 6]
Console là chỗ tốt nhất để thí nghiệm JS — không cần cài gì, không cần file. Nhược điểm: chỉ chạy được ở browser context, không gọi được Node API.
4. Cài Node và chạy file .js
Node là "JavaScript ngoài trình duyệt". Cài Node là bước cần để chạy script JS từ terminal, viết server, dùng npm package.
Cách cài khuyên dùng: nvm (Node Version Manager) — quản lý nhiều phiên bản Node song song. Lý do: project khác nhau yêu cầu Node version khác nhau, nvm cho phép switch nhanh.
# Cài nvm (macOS / Linux)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
# Khởi động lại terminal, sau đó:
nvm install 20 # cài Node 20 LTS
nvm use 20 # dùng version vừa cài
nvm alias default 20 # mặc định Node 20 cho terminal mới
# Kiểm tra
node --version # v20.x.x
npm --version # 10.x.x
Windows: tải installer từ nodejs.org, hoặc dùng nvm-windows (fork riêng).
Sau khi có Node, có 2 cách chạy code:
4.1. Node REPL
Gõ node trong terminal. Sẽ vào REPL giống browser Console:
$ node
Welcome to Node.js v20.10.0.
Type ".help" for more information.
> 1 + 1
2
> process.version
'v20.10.0'
> .exit
Thoát: Ctrl + D hoặc gõ .exit.
4.2. Chạy file .js
Tạo file hello.js:
console.log('Xin chào JavaScript');
Chạy:
$ node hello.js
Xin chào JavaScript
Đọc command-line argument qua process.argv:
// process.argv[0] = node binary path
// process.argv[1] = file path
// process.argv[2+] = args do user pass
const name = process.argv[2] ?? 'bạn';
console.log(`Xin chào, ${name}`);
$ node greet.js
Xin chào, bạn
$ node greet.js Việt
Xin chào, Việt
?? (nullish coalescing) là toán tử ES2020 — trả về vế phải nếu vế trái null hoặc
undefined. Chương 2 đào sâu.
5. package.json — manifest của project
Mỗi project JS "đàng hoàng" đều có file package.json ở gốc. Đây là tương đương pubspec.yaml
của Dart nhưng format JSON.
Tạo package.json bằng npm init:
# tạo project mới
mkdir my-app && cd my-app
# `-y` = accept default cho mọi câu hỏi
npm init -y
File package.json sinh ra trông như sau (đã thêm một số field thường gặp):
{
"name": "my-app",
"version": "1.0.0",
"description": "My first JS project",
"type": "module",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {},
"devDependencies": {}
}
Giải thích các field:
| Field | Vai trò |
|---|---|
name | Tên package. Nếu publish lên npm phải unique. |
version | SemVer (MAJOR.MINOR.PATCH). |
type | "module" = file .js dùng ESM. "commonjs" (default nếu thiếu) = CJS. |
main | Entry point khi ai đó require/import package này. |
scripts | Shortcut command. Chạy với npm run <name> (hoặc npm start/test cho 2 tên đặc biệt). |
dependencies | Package cần để chạy production (vd: express, react). |
devDependencies | Package chỉ cần khi dev (vd: vite, eslint, prettier). |
Luôn thêm "type": "module" cho project mới — ESM là tương lai. CommonJS là di sản từ Node 2009,
sẽ học để đọc code cũ ở chương 8.
6. npm ecosystem — cài package
npm (Node Package Manager) là tool cài package + registry trên npmjs.com (lưu hơn
2 triệu package). Có 3 lệnh quan trọng nhất:
# Cài vào dependencies (cần khi run)
npm install lodash
npm i lodash # shortcut
# Cài vào devDependencies (chỉ cần khi dev)
npm install -D prettier
npm i -D prettier # shortcut
# Cài global (có thể chạy lệnh từ bất kỳ đâu, nhưng nên hạn chế)
npm install -g pnpm
# Cài lại từ package.json (vd: pull repo về, clone xong)
npm install # đọc package.json + lock file, cài vào node_modules/
# Xoá package
npm uninstall lodash
Sau khi npm install lodash, có 3 thay đổi:
package.jsonthêm dòng"lodash": "^4.17.21"vào dependencies.package-lock.jsonđược tạo — lock chính xác version đã resolve, kèm hash kiểm tra integrity. Phải commit vào git.node_modules/mọc lên — chứa source code của lodash + các dependency của lodash. KHÔNG commit vào git (vào.gitignore).
npm install -g (global install) gây version conflict giữa các project. Ưu tiên cài
local vào project (default), rồi gọi qua npx hoặc npm script. Chỉ install global cho tool ít thay
đổi (vd pnpm, nvm).
6.1. npx — chạy package không cần install global
npx là tool đi kèm npm. Cho phép chạy package mà không cần install. Behind the scenes: nếu chưa có
local, npx download tạm vào cache rồi chạy.
# Chạy create-vite một lần để init project (không install global)
npx create-vite@latest my-app
# Chạy package từ devDependencies (không cần ./node_modules/.bin/eslint)
npx eslint .
6.2. pnpm và yarn — alternative
npm là default + ai cũng có. Nhưng có 2 alternative phổ biến:
| Tool | Highlight | Khi nào dùng |
|---|---|---|
npm | Default, ai cũng có, mature | Project nhỏ, không quan tâm tốc độ install |
pnpm | Nhanh hơn 2-3x npm (hard link, không duplicate), monorepo workspace support | Recommend cho project mới, monorepo |
yarn | Modern (Yarn Berry 4+), zero-install với PnP. Phổ biến trước pnpm. | Project cũ đã setup yarn, hoặc team đã quen |
Tất cả đều đọc/ghi cùng package.json + node_modules/. Khác nhau ở thuật toán resolve và
lock file. Chọn 1, đừng mix trong cùng 1 project.
7. Tooling baseline cho project mới
4 tool nên có cho mọi project JS hiện đại:
1. Prettier — format code tự động
Một code formatter có opinion (không cho config nhiều). Mục tiêu: kết thúc tranh cãi "tab vs space",
"single vs double quote". Cài: npm i -D prettier. Chạy: npx prettier --write ..
VS Code có extension "format on save" — bật là quên đi.
2. ESLint — phát hiện bug + bad pattern
Linter chuyên catch bug: biến không khai báo, dùng == thay ===, dead code... Cài:
npm i -D eslint @eslint/js. Setup config eslint.config.js. Chạy: npx eslint ..
3. Vite — dev server + bundler hiện đại
Tool dev của thế hệ ESM-native. Cực nhanh (esbuild prebundle, HMR <100ms). Tạo project mới:
npm create vite@latest. Chi tiết ở chương 8.
4. Git + .gitignore
Project JS bắt buộc có .gitignore với ít nhất: node_modules/, dist/,
.env. Chạy git init + tạo .gitignore ngay khi mới setup.
.gitignore mẫu cho JS project
# Dependencies
node_modules/
# Build output
dist/
build/
.next/
.nuxt/
# Logs
*.log
npm-debug.log*
yarn-debug.log*
# Env (chứa secret!)
.env
.env.local
.env.*.local
# Editor
.vscode/
.idea/
.DS_Store
# Cache
.cache/
.parcel-cache/
8. ESM vs CommonJS — intro
JavaScript có 2 module system. Chương 8 sẽ đào sâu, ở đây bạn cần biết để không bị confuse khi đọc code khác.
| — | CommonJS (cũ, Node) | ESM (chuẩn ES6+, mặc định mới) |
|---|---|---|
| Import | const x = require('./mod') | import x from './mod.js' |
| Export | module.exports = x | export default x / export {x} |
| Sync/Async | Sync | Async load |
| Browser support | ❌ (cần bundler) | ✅ native |
Khi nào dùng file .js | Mặc định Node, hoặc "type": "commonjs" | "type": "module" trong package.json, hoặc đổi tên .mjs |
Cho project mới: luôn set "type": "module" + dùng import/export. Khi đọc
code cũ vẫn dùng require: hiểu rằng đó là CommonJS, không hỗn loạn.
9. JS có "compile" không?
JavaScript là interpreted — không cần compile thành binary trước khi chạy. Tuy nhiên, modern engine (V8...) dùng JIT compilation (Just-In-Time): trong runtime, code nóng (hot path) được compile sang machine code để chạy nhanh.
Ngoài ra, ecosystem có 2 process biến đổi code phổ biến:
- Bundler (Vite/esbuild/Webpack/Rollup): gộp nhiều file
.js+ asset thành ít file.jsoutput. Thường minify để giảm size. Dùng cho production build. - Transpiler (Babel): convert code mới (ES2024) → cũ (ES5) để chạy trên trình duyệt cũ. Ngày nay ít cần — phần lớn browser support ES2020+. TypeScript compiler cũng là transpiler.
Sub-pillar này không đào sâu Babel (đã ít cần). Vite + tsc đủ cho 99% case. Chương 8 trình bày bundler chi tiết.
10. Cheat sheet chương 1
| Tình huống | Lệnh / cách làm |
|---|---|
| Thí nghiệm nhanh expression JS | Browser DevTools Console (F12) hoặc node REPL |
| Chạy 1 file JS từ terminal | node file.js |
| Init project JS mới | mkdir x && cd x && npm init -y |
| Cài package | npm i <name> (dep) hoặc npm i -D <name> (dev) |
| Chạy package 1 lần | npx <name> |
| Tạo SPA với Vite | npm create vite@latest |
Sửa file package.json để dùng ESM | Thêm "type": "module" |
| Đọc args command-line trong Node | process.argv[2] là arg đầu user truyền |
Bài tập
Bài 1 — Cài Node và verify
Cài Node 20 LTS qua nvm. Verify bằng:
node --version→ inv20.x.xnpm --version→ in10.x.x- Mở
nodeREPL, gõprocess.platform+process.arch, screenshot kết quả.
Gợi ý
nvm cài: curl -o- ...install.sh | bash. Restart terminal hoặc source ~/.zshrc. Sau đó nvm install 20.
Nếu Windows: dùng nvm-windows hoặc tải installer từ nodejs.org.
Bài 2 — So sánh API browser vs Node
Trong DevTools Console của Chrome, gõ navigator.userAgent và localStorage. Copy kết quả.
Sau đó trong Node REPL, gõ process.version và thử navigator. Quan sát error.
Trả lời: vì sao kết quả khác nhau?
Đáp án
Browser cung cấp navigator, localStorage, document như Web API — không phải spec JavaScript. Node không có chúng vì không có UI/browsing context.
Ngược lại, Node có process, fs, http — Node-specific API. Browser không có (vì lý do bảo mật + không cần thiết).
Đây là phân biệt lớp 3 (host environment) — cùng V8 engine nhưng API khác.
Bài 3 — Viết script greet.js
Tạo file greet.js nhận tên qua command-line args. Nếu không có tên thì default là "bạn".
Hiển thị: "Xin chào, [tên]". Test cả 2 case:
node greet.js→Xin chào, bạnnode greet.js Việt→Xin chào, Việt
Đáp án
const name = process.argv[2] ?? 'bạn';
console.log(`Xin chào, ${name}`);
?? trả vế phải nếu vế trái là null hoặc undefined. Nếu user không truyền arg, argv[2] = undefined → trả 'bạn'.
Bài 4 — Init project + npm script
Tạo folder mới, chạy npm init -y. Mở package.json:
- Sửa
"name"thành"my-greet". - Thêm
"type": "module". - Trong
"scripts", thêm"start": "node greet.js".
Chạy npm start — phải in greeting.
Đáp án package.json
{
"name": "my-greet",
"version": "1.0.0",
"type": "module",
"scripts": {
"start": "node greet.js"
}
}
Bài 5 — Cài + dùng date-fns
Trong project Bài 4, cài date-fns (helper xử lý ngày tháng). Viết file today.js:
- Import
formattừdate-fns. - Format ngày hôm nay theo định dạng Việt Nam:
'dd/MM/yyyy'. - In ra terminal:
"Hôm nay là 20/05/2026".
Sau đó xoá node_modules/, chạy npm install — quan sát package-lock.json đảm bảo cùng version cài lại.
Đáp án
// today.js
import { format } from 'date-fns';
const today = format(new Date(), 'dd/MM/yyyy');
console.log(`Hôm nay là ${today}`);
Cài: npm i date-fns. Chạy: node today.js.
package-lock.json đảm bảo reproducible build — ai clone repo cũng cài đúng version đã test.
Quiz
JavaScript và Java có liên quan gì về thiết kế ngôn ngữ không?
Xem đáp án
Không. Hai ngôn ngữ khác nhau hoàn toàn về paradigm (Java static class-based, JS dynamic prototype-based), runtime (JVM vs V8/SpiderMonkey), use case. Tên "JavaScript" chỉ là chiêu marketing 1995 của Netscape khi Java đang nổi.
console.log có ở mọi môi trường JavaScript không?
Xem đáp án
De-facto có (browser, Node, Deno, Bun), nhưng KHÔNG nằm trong ECMAScript spec. Nó là API do host environment cung cấp. Trong một runtime exotic (vd: embedded JS engine), có thể không có. Đây là phân biệt quan trọng: spec ≠ host API.
package.json và package-lock.json khác gì? File nào nên commit lên git?
Xem đáp án
package.json là declaration của developer — chứa version range (vd: "^1.2.0" = bất kỳ version 1.x.y với x≥2). package-lock.json là resolved version cụ thể (vd: "1.2.5") cùng hash.
Cả hai đều commit. Lock file đảm bảo team + CI cùng cài exact version → reproducible build. Bỏ lock = bug "works on my machine".
npm install -g <pkg> có nên dùng thường xuyên không?
Xem đáp án
Hạn chế. Lý do: package global cài cố định 1 version. Khi project A cần tool@1.0 và project B cần tool@2.0 → conflict.
Giải pháp: cài local devDependency + gọi qua npx hoặc npm scripts. Mỗi project tự có version riêng.
Ngoại lệ hợp lý: tool ít thay đổi version (nvm, pnpm chính nó) — cài global OK.
File .js không khai báo "type" trong package.json — Node coi nó là gì?
Xem đáp án
CommonJS (CJS), mặc định lịch sử của Node. Để dùng import/export (ESM), phải thêm "type": "module" vào package.json HOẶC đổi tên file thành .mjs (luôn ESM bất kể type).
Đối ngẫu: file .cjs luôn CommonJS, bất kể type.
ECMAScript phiên bản mới phát hành bao lâu một lần?
Xem đáp án
Mỗi năm, từ ES2015 trở đi. Mỗi tháng 6, TC39 finalize tất cả proposal đã đạt stage 4 thành phần của spec năm đó (ES2024, ES2025...). Trước 2015 thì chậm hơn nhiều (ES5 ra 2009, ES6 ra 2015 — 6 năm).
Trong browser, JS có truy cập trực tiếp file system local của user không?
Xem đáp án
Không. Browser sandbox JavaScript vì lý do bảo mật. Cách duy nhất để truy cập file:
<input type="file">+ user chủ động chọn file → đọc quaFileReaderAPI.- File System Access API (Chrome 86+) — user phải explicit grant permission.
Đây là lý do Node tồn tại: để JS có thể làm CLI tool, server, build script — những thứ cần I/O file thật.
Tổng kết
Sau chương 1, bạn nên đã nắm:
- Phân lớp 3 tầng: JS ngôn ngữ (ECMAScript spec) ≠ engine (V8/SpiderMonkey) ≠ host environment (browser/Node).
- Chạy JS ở đâu: browser DevTools Console, Node REPL,
node file.js. - Manifest project:
package.jsonvớitype,scripts,dependencies. - Cài package:
npm i,npm i -D,npx; commit lock file, ignorenode_modules/. - Tooling baseline: Prettier, ESLint, Vite — sẽ đào sâu chương 8.
- ESM vs CJS: ESM là tương lai, default cho project mới.
Khi bạn copy 1 đoạn code JS từ Stack Overflow, câu hỏi đầu tiên cần đặt là: "Code này chạy trong môi trường nào?" Browser? Node? Có dùng API host-specific (DOM, fs) không? Câu trả lời quyết định bạn có chạy được hay không.
Kết nối tới các chương khác
Mọi chương sau giả định bạn chạy được node file.js. Cụ thể:
- Chương 2-8 — code example chạy bằng Node + ESM.
- Chương 8 (Modules) — đào sâu ESM/CJS đã touch ở section 8 chương này.
- Chương 12 (TS Tooling) —
tsconfig.json≈ extension củapackage.jsoncho TypeScript. - Trụ cột CLI — terminal cơ bản đã là tiền đề. Nếu chưa quen, đọc trụ cột CLI trước.