JavaScript
JavaScript 全面学习手册
JavaScript 是前端开发的核心语言,Node.js 让 JS 也能写后端。 本文从基础语法到 ES6+ 高级特性,再到 Node.js 后端开发,覆盖变量、函数、对象、数组、异步、DOM 操作、Node.js 核心模块等全栈知识点。
一、基础语法
1.1 变量声明(var / let / const)
|
特性 |
var |
let |
const |
|---|---|---|---|
|
作用域 |
函数作用域 |
块级作用域 |
块级作用域 |
|
可重复声明 |
允许 |
不允许 |
不允许 |
|
可重新赋值 |
允许 |
允许 |
不允许 |
|
变量提升 |
提升并初始化为 undefined |
提升但未初始化(TDZ) |
提升但未初始化(TDZ) |
|
推荐使用 |
不推荐 |
需要修改的变量 |
默认首选 |
暂时性死区(TDZ):let/const 声明的变量在声明前不可访问,否则报 ReferenceError。
console.log(a); // undefined(var 提升)
var a = 1;
console.log(b); // ReferenceError(TDZ)
let b = 2;
const 注意:const 声明的对象/数组,其内部属性可以修改(引用不变即可)。
const obj = { name: 'Tom' };
obj.name = 'Jerry'; // 允许
obj = {}; // TypeError,不可重新赋值
const arr = [1, 2, 3];
arr.push(4); // 允许
arr = [5, 6]; // TypeError
1.2 数据类型
JS 共有 8 种数据类型,分为两大类:
原始类型(7种,不可变,存栈)
|
类型 |
示例 |
typeof 返回值 |
|---|---|---|
|
Number |
|
|
|
BigInt |
|
|
|
String |
|
|
|
Boolean |
|
|
|
undefined |
|
|
|
null |
|
|
|
Symbol |
|
|
引用类型(1种,可变,存堆)
|
类型 |
示例 |
typeof 返回值 |
|---|---|---|
|
Object |
|
|
类型判断方法
// typeof:判断原始类型(null 除外)
typeof 42; // "number"
typeof "hello"; // "string"
typeof undefined; // "undefined"
typeof null; // "object" ← 历史遗留 bug
typeof Symbol(); // "symbol"
typeof function(){}; // "function"
// instanceof:判断引用类型(检测原型链)
[] instanceof Array; // true
new Date() instanceof Date; // true
// Object.prototype.toString.call():最准确
Object.prototype.toString.call([]); // "[object Array]"
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(/abc/); // "[object RegExp]"
// Array.isArray():判断数组专用
Array.isArray([1, 2, 3]); // true
类型转换
显式转换(推荐):
// 转字符串
String(123); // "123"
(123).toString(); // "123"
123 + ""; // "123"(隐式,不推荐)
// 转数字
Number("123"); // 123
parseInt("123px"); // 123(解析直到非数字字符)
parseFloat("3.14");// 3.14
+"123"; // 123(隐式)
// 转布尔
Boolean(1); // true
!!"hello"; // true(双重否定)
Falsy 值(只有 6 个):false, 0, "", null, undefined, NaN
其余所有值都是 Truthy(包括 [], {}, "0", "false")。
1.3 运算符
算术运算符
|
运算符 |
说明 |
示例 |
|---|---|---|
|
|
加减乘除 |
|
|
|
取余 |
|
|
|
幂运算(ES7) |
|
|
|
自增/自减 |
前置 vs 后置有区别 |
比较运算符
// 严格相等(推荐)vs 抽象相等
=== // 类型相同 + 值相同
!== // 类型不同或值不同
== // 会做类型转换(避免使用)
"5" == 5; // true(字符串转数字)
"5" === 5; // false(类型不同)
null == undefined; // true(特殊情况)
null === undefined; // false
// Object.is() — 更严格的比较
Object.is(NaN, NaN); // true(=== 判断为 false)
Object.is(+0, -0); // false(=== 判断为 true)
逻辑运算符与短路
// &&:返回第一个 falsy 值,或最后一个 truthy 值
true && "hello"; // "hello"
false && "hello"; // false
0 && anything; // 0
// ||:返回第一个 truthy 值,或最后一个 falsy 值
null || "default"; // "default"(常用于默认值)
0 || 42; // 42
// ??:空值合并(ES2020),只对 null/undefined 生效
0 ?? 42; // 0(不认为 0 是空值)
null ?? 42; // 42
// ?.:可选链(ES2020),安全访问深层属性
obj?.prop?.nested; // undefined 而不是报错
obj?.method?.(); // 安全调用方法
展开运算符(...)
// 数组展开
const arr = [1, 2, 3];
const newArr = [...arr, 4, 5]; // [1, 2, 3, 4, 5]
const merged = [...arr1, ...arr2]; // 合并数组
const copy = [...original]; // 浅拷贝
Math.max(...numbers); // 展开为参数
// 对象展开
const obj = { a: 1, b: 2 };
const newObj = { ...obj, c: 3 }; // { a: 1, b: 2, c: 3 }
const mergedObj = { ...obj1, ...obj2 }; // 合并(同名属性后面覆盖前面)
1.4 条件与循环
条件语句
// if-else
if (score >= 90) {
console.log('优秀');
} else if (score >= 60) {
console.log('及格');
} else {
console.log('不及格');
}
// switch:=== 严格匹配
switch (fruit) {
case 'apple':
console.log('苹果');
break; // 必须 break,否则会穿透
case 'banana':
console.log('香蕉');
break;
default:
console.log('未知水果');
}
// 三元运算符
const result = score >= 60 ? '及格' : '不及格';
const displayName = name ?? '匿名用户'; // 处理 null/undefined
循环语句
// for — 经典循环
for (let i = 0; i < arr.length; i++) { }
// for...of — 遍历值(数组、字符串、Map、Set、类数组)
for (const item of arr) { }
for (const char of "hello") { }
for (const [key, val] of map) { } // Map 解构
// for...in — 遍历可枚举属性 key(对象)/ 索引(数组)
for (const key in obj) { } // 会遍历原型链,需配合 hasOwnProperty
for (const index in arr) { } // 不推荐用于数组
// while / do...while
while (condition) { }
do { } while (condition); // 至少执行一次
// 数组方法(函数式,推荐)
arr.forEach((item, index) => { }); // 遍历(不能 break)
arr.map(item => transform); // 映射
arr.filter(item => condition); // 过滤
arr.find(item => condition); // 查找
二、数组操作(高频)
2.1 核心方法速查
遍历与转换
|
方法 |
返回值 |
说明 |
是否改变原数组 |
|---|---|---|---|
|
|
undefined |
遍历每个元素 |
否 |
|
|
新数组 |
每个元素映射为新值 |
否 |
|
|
新数组 |
筛选满足条件的元素 |
否 |
|
|
累积值 |
累加/累积计算 |
否 |
|
|
第一个匹配元素 |
查找满足条件的第一个 |
否 |
|
|
第一个匹配索引 |
查找索引 |
否 |
|
|
boolean |
是否有任意元素满足 |
否 |
|
|
boolean |
是否所有元素都满足 |
否 |
增删改
|
方法 |
返回值 |
说明 |
是否改变原数组 |
|---|---|---|---|
|
|
新 length |
尾部追加 |
是 |
|
|
移除的元素 |
尾部移除 |
是 |
|
|
新 length |
头部追加 |
是 |
|
|
移除的元素 |
头部移除 |
是 |
|
|
被删元素数组 |
删除/插入/替换 |
是 |
|
|
新数组 |
截取子数组 |
否 |
|
|
新数组 |
合并数组 |
否 |
2.2 核心方法详解
map — 映射转换
const nums = [1, 2, 3];
const doubled = nums.map(n => n * 2); // [2, 4, 6]
const objects = nums.map(n => ({ value: n })); // 转为对象数组,箭头函数返回对象需加括号
const withIndex = nums.map((n, i) => `${i}: ${n}`); // 第二个参数是索引
filter — 条件过滤
const nums = [1, 2, 3, 4, 5, 6];
const evens = nums.filter(n => n % 2 === 0); // [2, 4, 6]
const active = users.filter(u => u.active); // 过滤 active 为 true
const filtered = arr.filter(Boolean); // 快速去除 falsy 值
reduce — 万能的累积计算
// 求和
const sum = [1, 2, 3].reduce((acc, cur) => acc + cur, 0); // 6
// 分组(高频实战)
const groupByType = items.reduce((acc, item) => {
(acc[item.type] ??= []).push(item);
return acc;
}, {});
// 数组扁平化
const flat = [[1, 2], [3, 4]].reduce((acc, cur) => acc.concat(cur), []); // [1,2,3,4]
// 管道函数(函数组合)
const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);
const result = pipe(addOne, double, square)(3);
find / findIndex — 查找
const users = [{ id: 1, name: 'A' }, { id: 2, name: 'B' }];
users.find(u => u.id === 2); // { id: 2, name: 'B' }
users.findIndex(u => u.id === 2); // 1
users.find(u => u.id === 99); // undefined(找不到)
users.findIndex(u => u.id === 99); // -1
some / every — 判断
const nums = [1, 2, 3, 4, 5];
nums.some(n => n > 3); // true(存在大于3的)
nums.every(n => n > 0); // true(所有都大于0)
nums.every(n => n > 3); // false
2.3 排序与去重
// sort — 默认按字符串排序!
[1, 2, 10].sort(); // [1, 10, 2] ← 坑!
[1, 2, 10].sort((a, b) => a - b); // [1, 2, 10] 升序
[1, 2, 10].sort((a, b) => b - a); // [10, 2, 1] 降序
// 对象数组排序
users.sort((a, b) => a.age - b.age); // 按年龄升序
users.sort((a, b) => a.name.localeCompare(b.name)); // 按名字字母序
// 去重
[...new Set(arr)]; // 最简单(仅基本类型)
const unique = arr.filter((item, index) => arr.indexOf(item) === index); // 通用
2.4 数组方法链式调用
const result = [1, 2, 3, 4, 5, 6]
.filter(n => n % 2 === 0) // [2, 4, 6]
.map(n => n * 10) // [20, 40, 60]
.reduce((sum, n) => sum + n, 0); // 120
三、函数
3.1 函数声明方式
// 1. 函数声明(会提升)
function add(a, b) { return a + b; }
// 2. 函数表达式(不提升)
const add = function(a, b) { return a + b; };
// 3. 箭头函数(ES6,不提升,无自己的 this/arguments)
const add = (a, b) => a + b; // 单表达式可省略 {} 和 return
const greet = name => `Hello, ${name}`; // 单参数可省略 ()
const createObj = name => ({ name }); // 返回对象需括号包裹
const noop = () => {}; // 无参数
// 4. IIFE 立即执行函数
(function() { console.log('旧式'); })();
(() => { console.log('新式'); })(); // 箭头函数 IIFE
3.2 箭头函数 vs 普通函数
|
特性 |
普通函数 |
箭头函数 |
|---|---|---|
|
this |
动态(调用时确定) |
静态(定义时捕获外层 this) |
|
arguments |
有 |
无(用 rest 替代) |
|
new |
可作构造函数 |
不可 |
|
prototype |
有 |
无 |
|
适用 |
对象方法、构造函数 |
回调、map/filter、闭包 |
// this 区别 — 高频面试题
const obj = {
name: 'Tom',
fn1: function() { console.log(this.name); }, // this 指向 obj
fn2: () => { console.log(this.name); }, // this 指向外层(window/global)
};
obj.fn1(); // 'Tom'
obj.fn2(); // undefined(外层没有 name)
// 箭头函数在回调中很有用
class Component {
constructor() {
this.name = 'MyComponent';
// 箭头函数自动绑定 this
document.addEventListener('click', () => {
console.log(this.name); // 正确指向 Component
});
}
}
3.3 函数参数
// 默认参数(ES6)
function greet(name = 'guest') { return `Hi, ${name}`; }
greet(); // "Hi, guest"
greet('Tom'); // "Hi, Tom"
// 剩余参数 Rest(ES6)
function sum(...nums) { return nums.reduce((a, b) => a + b, 0); }
sum(1, 2, 3, 4); // 10
// 参数解构
function print({ name, age = 18 }) { console.log(name, age); }
print({ name: 'Tom' }); // Tom 18
3.4 闭包(Closure)
闭包 = 函数 + 其外部变量的引用。函数内部可以访问外部作用域的变量,即使外部函数已执行完毕。
// 经典例子:创建私有变量
function createCounter() {
let count = 0; // 私有变量,外部无法直接访问
return {
increment: () => ++count,
decrement: () => --count,
get: () => count,
};
}
const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.get(); // 2
// count 无法直接访问
// 实际应用:防抖和节流(见 5.3 节)
// 经典面试坑:循环中的闭包
for (var i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 0); // 打印 5 个 5!
}
// 解决方案 1:使用 let(块级作用域)
for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 0); // 0, 1, 2, 3, 4
}
// 解决方案 2:IIFE
for (var i = 0; i < 5; i++) {
((j) => setTimeout(() => console.log(j), 0))(i); // 0, 1, 2, 3, 4
}
3.5 高阶函数
接收函数作为参数 或 返回函数 的函数。
// 函数作为参数(回调)
[1, 2, 3].map(n => n * 2);
// 函数作为返回值(柯里化)
const multiply = a => b => a * b;
const double = multiply(2);
double(5); // 10
double(8); // 16
// 函数组合(compose / pipe)
const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);
const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);
3.6 this 指向全解
|
调用方式 |
this 指向 |
|---|---|
|
普通函数调用 |
|
|
方法调用 |
|
|
构造函数 |
新创建的实例 |
|
|
第一个参数 |
|
箭头函数 |
定义时的外层 this |
|
DOM 事件处理 |
触发事件的元素 |
// call / apply / bind
function greet(greeting, punct) {
console.log(greeting + ', ' + this.name + punct);
}
const user = { name: 'Tom' };
greet.call(user, 'Hi', '!'); // "Hi, Tom!" (逐个传参)
greet.apply(user, ['Hi', '!']); // "Hi, Tom!" (数组传参)
const bound = greet.bind(user, 'Hi');
bound('!'); // "Hi, Tom!" (返回新函数,可预设参数)
四、对象与解构
4.1 对象创建与属性
// 字面量(最常用)
const obj = { name: 'Tom', age: 18 };
// 属性简写(ES6)
const name = 'Tom', age = 18;
const obj = { name, age }; // 等价 { name: name, age: age }
// 计算属性名(ES6)
const key = 'dynamic';
const obj = { [key]: 'value' }; // { dynamic: 'value' }
// 方法简写(ES6)
const obj = {
greet() { return 'hello'; }, // 等价 greet: function() { ... }
};
4.2 解构赋值(ES6)
// 对象解构
const { name, age, city = '北京' } = user; // 默认值
const { name: userName } = user; // 重命名
const { address: { city } } = user; // 深层解构
// 函数参数解构
function display({ name, age = 18 }) { }
// 数组解构
const [first, second, ...rest] = [1, 2, 3, 4, 5]; // first=1, rest=[3,4,5]
const [a, , b] = [1, 2, 3]; // 跳过元素:a=1, b=3
// 交换变量(无需临时变量)
let x = 1, y = 2;
[y, x] = [x, y]; // x=2, y=1
4.3 对象方法与遍历
// 常用方法
Object.keys(obj); // 返回可枚举属性名数组
Object.values(obj); // 返回可枚举属性值数组(ES8)
Object.entries(obj); // 返回 [key, value] 对数组(ES8)
Object.assign(target, ...sources); // 浅合并(ES6)
Object.freeze(obj); // 冻结对象(不可修改)
Object.seal(obj); // 密封对象(不可添加/删除,可修改已有属性)
obj.hasOwnProperty(key);// 判断自有属性(非继承)
// 遍历写法
for (const key in obj) { if (obj.hasOwnProperty(key)) { ... } }
for (const [key, val] of Object.entries(obj)) { ... }
Object.keys(obj).forEach(key => { const val = obj[key]; ... });
4.4 原型与继承
// 每个对象都有 __proto__,指向其构造函数的 prototype
// 每个函数都有 prototype 属性
// ES6 Class(语法糖)
class Animal {
constructor(name) { this.name = name; }
speak() { console.log(`${this.name} makes a sound.`); }
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // 必须先调用 super
this.breed = breed;
}
speak() { console.log(`${this.name} barks.`); }
fetch() { return `${this.name} is fetching!`; }
}
// 私有字段(ES2022,以 # 开头)
class Counter {
#count = 0; // 真正的私有字段
increment() { this.#count++; }
get value() { return this.#count; }
}
// getter / setter
class User {
constructor(first, last) { this.first = first; this.last = last; }
get fullName() { return `${this.first} ${this.last}`; }
set fullName(val) { [this.first, this.last] = val.split(' '); }
}
4.5 Set、Map、WeakMap
// Set — 不重复的值的集合
const set = new Set([1, 2, 2, 3]); // Set { 1, 2, 3 }
set.add(4); set.has(2); set.delete(3); set.size; set.clear();
for (const val of set) { } // 可迭代
[...new Set(arr)]; // 数组去重
// Map — 键值对,键可以是任意类型
const map = new Map();
map.set('key', 'value');
map.set({ id: 1 }, 'obj value'); // 对象作为键
map.get('key'); map.has('key'); map.delete('key'); map.size;
for (const [k, v] of map) { }
// WeakMap — 键必须是对象,不阻止 GC,不可迭代
const wm = new WeakMap();
wm.set(obj, metadata); // obj 被回收时,metadata 自动释放
五、异步编程
5.1 事件循环(Event Loop)
JS 是单线程语言,通过事件循环实现异步非阻塞。
调用栈(Call Stack)→ 执行同步代码
↓ 清空后
微任务队列(Microtask Queue):Promise.then/catch/finally、queueMicrotask、MutationObserver
↓ 清空后(每轮只取一个宏任务,但先清空所有微任务)
宏任务队列(Macrotask Queue):setTimeout、setInterval、I/O、UI 渲染、setImmediate(Node)
执行顺序(必考面试题):
console.log(1);
setTimeout(() => console.log(2), 0);
Promise.resolve().then(() => console.log(3));
console.log(4);
// 输出:1, 4, 3, 2
// 解释:同步 → 微任务(Promise) → 宏任务(setTimeout)
5.2 Promise
基本用法
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const success = Math.random() > 0.5;
success ? resolve('成功') : reject(new Error('失败'));
}, 1000);
});
promise
.then(result => console.log(result))
.catch(err => console.error(err))
.finally(() => console.log('无论成功失败都执行'));
Promise 静态方法
// Promise.all — 全部成功才成功,一个失败则失败
Promise.all([fetch('/api/1'), fetch('/api/2')])
.then(([res1, res2]) => console.log('全部完成'));
// ⚠️ 注意:有一个 reject 就立即失败,其他请求可能还在进行
// Promise.allSettled — 全部 settled 后返回(不管成功失败)
Promise.allSettled([fetch1, fetch2])
.then(results => {
results.forEach(r => {
if (r.status === 'fulfilled') console.log(r.value);
if (r.status === 'rejected') console.log(r.reason);
});
});
// Promise.race — 第一个 settled 的 Promise
Promise.race([fetchWithTimeout(url, 5000), timeout(3000)]);
// Promise.any — 第一个成功的 Promise,全失败则 AggregateError
Promise.any([fetch('/api/a'), fetch('/api/b')])
.then(first => console.log('任意一个成功'));
// Promise.resolve / Promise.reject (快捷创建)
Promise.resolve(42);
Promise.reject(new Error('oops'));
Promise 链式调用
fetchUser(id)
.then(user => fetchOrders(user.id))
.then(orders => processOrders(orders))
.then(result => console.log(result))
.catch(err => console.error('任意环节出错:', err));
// ⚠️ 链式调用 vs 嵌套回调的对比体现代码可读性改进
手写 Promise(高频手撕)
class MyPromise {
constructor(executor) {
this.state = 'pending'; // pending → fulfilled / rejected
this.value = undefined;
this.callbacks = [];
const resolve = (val) => {
if (this.state !== 'pending') return;
this.state = 'fulfilled';
this.value = val;
this.callbacks.forEach(cb => cb.onFulfilled(val));
};
const reject = (err) => {
if (this.state !== 'pending') return;
this.state = 'rejected';
this.value = err;
this.callbacks.forEach(cb => cb.onRejected(err));
};
try { executor(resolve, reject); }
catch (e) { reject(e); }
}
then(onFulfilled, onRejected) {
if (this.state === 'fulfilled') onFulfilled?.(this.value);
else if (this.state === 'rejected') onRejected?.(this.value);
else this.callbacks.push({ onFulfilled, onRejected });
}
catch(onRejected) { return this.then(null, onRejected); }
}
5.3 async / await(推荐)
async/await 是 Promise 的语法糖,让异步代码看起来像同步代码。
// 基本用法
async function fetchData() {
try {
const user = await fetchUser(id); // 等待 Promise resolve
const orders = await fetchOrders(user.id);
return { user, orders };
} catch (err) {
console.error('出错:', err);
// 统一错误处理
}
}
// 并行 vs 串行
// ❌ 串行(总耗时 = sum)
const a = await fetchA();
const b = await fetchB();
// ✅ 并行(总耗时 = max)
const [a, b] = await Promise.all([fetchA(), fetchB()]);
// 顶层 await(ES2022,ES Module 中可用)
const config = await fetchConfig();
常见 async/await 陷阱
// 循环中使用 await
// ❌ 串行(n 个请求依次执行)
for (const item of items) {
await process(item);
}
// ✅ 并行(n 个请求同时发起)
await Promise.all(items.map(item => process(item)));
// ✅ 分批并发(控制并发数,避免请求过多)
for (let i = 0; i < items.length; i += CHUNK) {
await Promise.all(items.slice(i, i + CHUNK).map(process));
}
5.4 防抖与节流(高频手撕 + 实战)
// 防抖(debounce):连续触发只执行最后一次
// 场景:搜索框输入、窗口 resize
function debounce(fn, delay = 300) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
// 使用
input.addEventListener('input', debounce(onSearch, 500));
// 节流(throttle):固定时间间隔执行一次
// 场景:滚动事件、按钮防重复点击
function throttle(fn, delay = 300) {
let last = 0;
return function(...args) {
const now = Date.now();
if (now - last >= delay) {
last = now;
fn.apply(this, args);
}
};
}
// 使用
window.addEventListener('scroll', throttle(onScroll, 200));
六、DOM 操作
6.1 元素选择
// 单个元素
document.getElementById('id');
document.querySelector('.class'); // CSS 选择器,返回第一个匹配
// 多个元素
document.getElementsByClassName('class'); // HTMLCollection(动态,不推荐)
document.getElementsByTagName('div'); // HTMLCollection(动态,不推荐)
document.querySelectorAll('.class'); // NodeList(静态,推荐)
6.2 DOM 遍历
element.parentElement; // 父元素
element.children; // 子元素集合
element.firstElementChild; // 第一个子元素
element.lastElementChild; // 最后一个子元素
element.previousElementSibling; // 上一个兄弟元素
element.nextElementSibling; // 下一个兄弟元素
6.3 DOM 增删改
// 创建
const div = document.createElement('div');
div.className = 'container';
div.innerHTML = '<span>内容</span>'; // 简单但注意 XSS
div.textContent = '纯文本'; // 安全,自动转义
// 插入
parent.appendChild(newNode); // 末尾追加
parent.insertBefore(newNode, referenceNode); // 在 referenceNode 前插入
parent.prepend(node); // 头部插入(现代)
parent.append(node); // 尾部插入(现代)
referenceNode.before(node); // 在前面插入(现代)
referenceNode.after(node); // 在后面插入(现代)
// 替换 / 删除 / 克隆
oldNode.replaceWith(newNode);
element.remove(); // 直接移除(现代)
parent.removeChild(child); // 传统方式
const clone = element.cloneNode(true); // true = 深克隆
6.4 属性与样式操作
// 属性
el.getAttribute('data-id');
el.setAttribute('data-id', '123');
el.removeAttribute('data-id');
el.dataset.id; // data-id → dataset.id
el.dataset.userName; // data-user-name → dataset.userName(驼峰)
// 类名
el.classList.add('active');
el.classList.remove('hidden');
el.classList.toggle('dark');
el.classList.contains('active');
// 样式
el.style.color = 'red';
el.style.cssText = 'color: red; font-size: 16px';
const styles = getComputedStyle(el); // 读取计算后的样式(只读)
// 尺寸与位置
el.offsetWidth / el.offsetHeight; // 含 border
el.clientWidth / el.clientHeight; // 不含 border
el.scrollWidth / el.scrollHeight; // 内容实际大小
el.getBoundingClientRect(); // 相对视口的位置 { top, left, bottom, right, width, height }
6.5 事件处理
// 添加事件
el.addEventListener('click', handler);
el.addEventListener('click', handler, { once: true }); // 只触发一次
el.addEventListener('click', handler, { passive: true }); // 不阻止默认行为(滚动性能优化)
// 移除事件
el.removeEventListener('click', handler);
// 常用事件
// 鼠标:click, dblclick, mousedown, mouseup, mousemove, mouseenter, mouseleave, contextmenu
// 键盘:keydown, keyup, keypress(已废弃)
// 表单:input, change, focus, blur, submit, reset
// 文档:DOMContentLoaded, load, beforeunload
// 触摸:touchstart, touchmove, touchend
// 事件委托(高频优化技巧)
// 利用事件冒泡,在父元素上监听子元素的事件
parent.addEventListener('click', (e) => {
if (e.target.matches('.delete-btn')) {
deleteItem(e.target.dataset.id);
}
});
// 好处:减少监听器数量、动态添加的子元素自动生效
事件传播三阶段
捕获阶段(Capture) → 目标阶段(Target) → 冒泡阶段(Bubble)
↓ ↓
addEventListener(..., true) addEventListener(..., false) ← 默认
// 阻止事件传播
e.stopPropagation(); // 阻止冒泡
e.stopImmediatePropagation(); // 阻止冒泡 + 同元素其他监听器
e.preventDefault(); // 阻止默认行为(链接跳转、表单提交等)
七、AJAX 请求
7.1 XMLHttpRequest(传统)
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/users');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log(JSON.parse(xhr.responseText));
}
}
};
xhr.onerror = () => console.error('网络错误');
xhr.send(JSON.stringify({ key: 'value' }));
7.2 Fetch API(现代,推荐)
// GET 请求
const response = await fetch('/api/users');
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json(); // 还有 .text() .blob() .formData() .arrayBuffer()
// POST 请求
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Tom', age: 18 }),
});
const result = await response.json();
// 上传文件
const formData = new FormData();
formData.append('file', fileInput.files[0]);
await fetch('/api/upload', { method: 'POST', body: formData });
// 超时控制(fetch 不原生支持 timeout!)
function fetchWithTimeout(url, options = {}, timeout = 5000) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeout);
return fetch(url, { ...options, signal: controller.signal })
.finally(() => clearTimeout(timer));
}
// 请求拦截(用 Wrapper 封装)
async function request(url, options = {}) {
const token = localStorage.getItem('token');
options.headers = {
...options.headers,
Authorization: token ? `Bearer ${token}` : '',
};
const res = await fetch(url, options);
if (res.status === 401) { /* 跳转登录 */ }
return res.json();
}
7.3 Axios(最流行的第三方 HTTP 库)
// 安装:npm install axios
// 基本用法
const res = await axios.get('/api/users');
const res = await axios.post('/api/users', { name: 'Tom' });
const res = await axios.put('/api/users/1', { name: 'Jerry' });
const res = await axios.delete('/api/users/1');
// 全局拦截器
axios.interceptors.request.use(
config => {
config.headers.Authorization = `Bearer ${getToken()}`;
return config;
},
error => Promise.reject(error)
);
axios.interceptors.response.use(
res => res.data, // 直接返回 data
error => {
if (error.response?.status === 401) { /* 处理未授权 */ }
return Promise.reject(error);
}
);
// 取消请求
const controller = new AbortController();
axios.get('/api/users', { signal: controller.signal });
controller.abort();
// 超时
axios.get('/api/users', { timeout: 5000 });
7.4 跨域解决方案(CORS)
// CORS(服务端设置响应头)
// Access-Control-Allow-Origin: https://example.com
// Access-Control-Allow-Methods: GET, POST, PUT, DELETE
// Access-Control-Allow-Headers: Content-Type, Authorization
// Access-Control-Allow-Credentials: true
// 携带 Cookie 的跨域请求
fetch(url, { credentials: 'include' }); // fetch
axios.defaults.withCredentials = true; // axios
// JSONP(仅 GET,已过时)
// 原理:利用 <script> 标签不受同源策略限制
// 代理服务器:开发环境 proxy(Vite/Webpack devServer)
八、Node.js 核心
Node.js = JS 运行时 + 服务器能力。 让 JavaScript 脱离浏览器,用在后端开发、工具脚本、CLI 应用等场景。
8.1 内置模块速览
|
模块 |
用途 |
高频 API |
|---|---|---|
|
fs |
文件系统操作 |
|
|
path |
路径处理 |
|
|
http |
HTTP 服务器/客户端 |
|
|
os |
操作系统信息 |
|
|
process |
进程信息 |
|
|
crypto |
加密与哈希 |
|
|
events |
事件发射器 |
|
|
stream |
流处理 |
|
|
child_process |
子进程 |
|
|
url |
URL 解析 |
|
|
util |
实用工具 |
|
|
buffer |
二进制数据 |
|
8.2 文件操作(fs + path)
const fs = require('fs');
const fsp = require('fs/promises'); // Promise 版本(推荐)
const path = require('path');
// === 路径处理 ===
path.join('/foo', 'bar', 'baz'); // \foo\bar\baz(跨平台)
path.resolve('src', 'index.js'); // 绝对路径
path.basename('/foo/bar/file.txt'); // 'file.txt'
path.dirname('/foo/bar/file.txt'); // '/foo/bar'
path.extname('file.txt'); // '.txt'
path.parse('/foo/bar/file.txt');
// { root: '/', dir: '/foo/bar', base: 'file.txt', ext: '.txt', name: 'file' }
// === 同步 API(简单脚本用)===
const data = fs.readFileSync('file.txt', 'utf-8');
fs.writeFileSync('file.txt', 'hello', 'utf-8');
fs.existsSync('file.txt'); // true/false
fs.mkdirSync('dir', { recursive: true });// 递归创建目录
const files = fs.readdirSync('dir'); // 列出目录内容
// === 异步 API(服务器用,推荐 Promise 版本)===
await fsp.readFile('file.txt', 'utf-8');
await fsp.writeFile('file.txt', 'hello', 'utf-8');
await fsp.mkdir('dir', { recursive: true });
const files = await fsp.readdir('dir');
const stat = await fsp.stat('file.txt'); // 文件信息(大小、修改时间等)
await fsp.rm('file.txt'); // 删除文件
await fsp.rm('dir', { recursive: true }); // 递归删除目录
// === 流式读写(大文件)===
const readStream = fs.createReadStream('big.txt', { highWaterMark: 64 * 1024 });
const writeStream = fs.createWriteStream('copy.txt');
readStream.pipe(writeStream);
// 或使用 pipeline
const { pipeline } = require('stream/promises');
await pipeline(readStream, writeStream);
8.3 HTTP 服务器
// 原生 http 模块
const http = require('http');
const server = http.createServer((req, res) => {
const { method, url, headers } = req;
// 解析 body
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', () => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ method, url, body: JSON.parse(body || '{}') }));
});
});
server.listen(3000, () => console.log('http://localhost:3000'));
8.4 Express(最流行的 Node 框架)
npm install express
const express = require('express');
const app = express();
// === 中间件 ===
app.use(express.json()); // 解析 JSON body
app.use(express.urlencoded({ extended: true })); // 解析 URL-encoded
app.use(express.static('public')); // 静态文件
app.use((req, res, next) => { // 自定义中间件(日志、CORS、鉴权等)
console.log(`${req.method} ${req.url}`);
next();
});
// === 路由 ===
app.get('/api/users', async (req, res) => {
const users = await db.query('SELECT * FROM users');
res.json({ code: 0, data: users });
});
app.get('/api/users/:id', (req, res) => {
const { id } = req.params; // 路径参数
const { fields } = req.query; // 查询参数 ?fields=name,age
// ...
});
app.post('/api/users', async (req, res) => {
const { name, email } = req.body; // POST body
// ...
res.status(201).json({ code: 0, data: user });
});
app.put('/api/users/:id', (req, res) => { /* 更新 */ });
app.delete('/api/users/:id', (req, res) => { /* 删除 */ });
// === 路由模块化 ===
// routers/users.js
const router = express.Router();
router.get('/', listUsers);
router.post('/', createUser);
module.exports = router;
// app.js
app.use('/api/users', require('./routers/users'));
// === 错误处理 ===
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(err.status || 500).json({ code: -1, message: err.message });
});
// === 启动 ===
app.listen(3000, () => console.log('Server: http://localhost:3000'));
8.5 读写 JSON 文件(实用工具)
const fsp = require('fs/promises');
// 读取 JSON
async function readJSON(filepath) {
try {
const raw = await fsp.readFile(filepath, 'utf-8');
return JSON.parse(raw);
} catch (err) {
if (err.code === 'ENOENT') return null; // 文件不存在
throw err;
}
}
// 写入 JSON
async function writeJSON(filepath, data) {
await fsp.writeFile(filepath, JSON.stringify(data, null, 2), 'utf-8');
}
// 使用
const config = await readJSON('config.json');
config.lastRun = new Date().toISOString();
await writeJSON('config.json', config);
8.6 npm 包管理
# 初始化项目
npm init -y # 创建 package.json
# 安装包
npm install express # 安装到 dependencies
npm install -D nodemon # 安装到 devDependencies
npm install -g typescript # 全局安装
npm install express@4.18.2 # 指定版本
npm install # 根据 package.json 安装所有依赖
# 卸载
npm uninstall express
npm uninstall -D nodemon
# scripts(在 package.json 中定义)
{
"scripts": {
"dev": "nodemon app.js",
"start": "node app.js",
"test": "jest",
"build": "webpack --mode production"
}
}
npm run dev # 运行自定义脚本
npm start # start/test/stop/restart 可省略 run
# npx — 运行本地安装的包
npx create-react-app my-app
npx jest
# 版本号规则(语义化版本 Semver)
# MAJOR.MINOR.PATCH(主版本.次版本.补丁版本)
# ^1.2.3 = 1.x.x(允许次版本和补丁升级,推荐)
# ~1.2.3 = 1.2.x(只允许补丁升级)
# 1.2.3 = 精确版本
8.7 模块系统(CommonJS vs ESM)
// === CommonJS(传统,Node 默认)===
// 导出
module.exports = { add, subtract };
exports.add = add; // exports 是 module.exports 的引用
// 导入
const { add } = require('./math');
const math = require('./math');
// === ES Module(现代标准)===
// package.json 需设置 "type": "module",或文件扩展名为 .mjs
// 导出
export const add = (a, b) => a + b;
export default function greet() { }
// 导入
import greet, { add } from './math.js'; // 注意要写 .js 后缀!
import * as math from './math.js';
// 动态导入(两个模块系统都支持)
const module = await import('./heavy-module.js');
九、ES6+ 常用特性速查
|
特性 |
版本 |
说明 |
|---|---|---|
|
|
ES6 |
块级作用域 |
|
箭头函数 |
ES6 |
|
|
模板字符串 |
ES6 |
|
|
解构赋值 |
ES6 |
|
|
展开运算符 |
ES6 |
|
|
默认参数 |
ES6 |
|
|
剩余参数 |
ES6 |
|
|
Class |
ES6 |
语法糖,本质仍是原型继承 |
|
Promise |
ES6 |
异步流程控制 |
|
Symbol |
ES6 |
唯一标识符 |
|
Set / Map |
ES6 |
新集合类型 |
|
|
ES8 |
Promise 语法糖 |
|
|
ES8 |
对象遍历 |
|
|
ES10 |
数组扁平化 |
|
|
ES10 |
entries → 对象 |
|
可选链 |
ES2020 |
安全访问深层属性 |
|
空值合并 |
ES2020 |
仅对 null/undefined 生效 |
|
逻辑赋值 |
ES2021 |
条件赋值简写 |
|
|
ES2022 |
负索引访问( |
|
私有字段 |
ES2022 |
|
十、学习路线与推荐资源
学习路线图
阶段一:基础语法(1-2 周)
├── 变量声明、数据类型、运算符
├── 条件语句、循环
└── 函数基础、作用域
阶段二:核心进阶(2-4 周)
├── 数组方法(map/filter/reduce 等 6 核心方法)
├── 对象操作、解构、展开
├── 闭包、this、高阶函数
├── Promise、async/await
└── ES6+ 新特性全览
阶段三:浏览器实战(2-3 周)
├── DOM 操作、事件处理
├── AJAX / fetch / axios
├── 跨域与 CORS
└── 防抖节流、事件委托等常用模式
阶段四:Node.js 后端(3-4 周)
├── 核心模块(fs/path/http/events/stream)
├── Express 框架
├── 数据库操作(MySQL/MongoDB/Redis)
└── npm 包管理、项目结构
阶段五:进阶专题(持续学习)
├── 原型链与继承
├── 事件循环与异步原理
├── 手撕常见 API(Promise/防抖节流/deepClone/bind)
├── 模块化(CommonJS/ESM/打包工具)
└── TypeScript 入门
推荐资源
文档与教程:
- MDN Web Docs(最权威的 JS 参考文档)
- JavaScript.info(现代 JS 教程,中文版)
- Node.js 官方文档
- Express.js 官方指南
练习平台:
- LeetCode(算法练习,JS 解题)
- CodeWars / HackerRank(编程练习)
- JS Bin / CodePen / CodeSandbox(在线编码)
书籍推荐:
- 《JavaScript 高级程序设计》(红宝书)
- 《你不知道的 JavaScript》(上中下)
- 《JavaScript 设计模式与开发实践》
- 《Node.js 实战》
GitHub 仓库:
- 30-seconds-of-code(常用代码片段)
- javascript-questions(JS 面试题精选)
- You-Dont-Know-JS(深入理解 JS)