跳转到内容

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

42, 3.14, NaN, Infinity

"number"

BigInt

9007199254740991n

"bigint"

String

"hello", 'world', `模板`

"string"

Boolean

true, false

"boolean"

undefined

let a;

"undefined"

null

let a = null;

"object"(历史 bug)

Symbol

Symbol('id')

"symbol"

引用类型(1种,可变,存堆)

类型

示例

typeof 返回值

Object

{}, [], function(){}, new Date()

"object" / "function"

类型判断方法

// 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 运算符

算术运算符

运算符

说明

示例

+-*/

加减乘除

10 / 33.333...

%

取余

10 % 31

**

幂运算(ES7)

2 ** 101024

++--

自增/自减

前置 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 核心方法速查

遍历与转换

方法

返回值

说明

是否改变原数组

forEach

undefined

遍历每个元素

map

新数组

每个元素映射为新值

filter

新数组

筛选满足条件的元素

reduce

累积值

累加/累积计算

find

第一个匹配元素

查找满足条件的第一个

findIndex

第一个匹配索引

查找索引

some

boolean

是否有任意元素满足

every

boolean

是否所有元素都满足

增删改

方法

返回值

说明

是否改变原数组

push

新 length

尾部追加

pop

移除的元素

尾部移除

unshift

新 length

头部追加

shift

移除的元素

头部移除

splice

被删元素数组

删除/插入/替换

slice

新数组

截取子数组

concat

新数组

合并数组

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 指向

普通函数调用 fn()

undefined(严格模式)/ window(非严格)

方法调用 obj.fn()

obj

构造函数 new Fn()

新创建的实例

call / apply / bind

第一个参数

箭头函数 () =&gt; {}

定义时的外层 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

文件系统操作

readFile, writeFile, readdir, mkdir, existsSync, createReadStream

path

路径处理

join, resolve, basename, dirname, extname, parse

http

HTTP 服务器/客户端

createServer, request, listen

os

操作系统信息

cpus, freemem, totalmem, platform, homedir, networkInterfaces

process

进程信息

env, argv, cwd, exit, pid, nextTick

crypto

加密与哈希

createHash, createHmac, randomBytes, pbkdf2

events

事件发射器

EventEmitter, on, emit, once, removeListener

stream

流处理

Readable, Writable, Transform, pipeline

child_process

子进程

exec, spawn, fork, execFile

url

URL 解析

URL, URLSearchParams

util

实用工具

promisify, format, inherits, types

buffer

二进制数据

Buffer.from, Buffer.alloc, Buffer.concat

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+ 常用特性速查

特性

版本

说明

let / const

ES6

块级作用域

箭头函数

ES6

=&gt; 语法,无自己的 this

模板字符串

ES6

`Hello, ${name}`

解构赋值

ES6

const { a, b } = obj

展开运算符

ES6

...arr, ...obj

默认参数

ES6

function fn(x = 1) {}

剩余参数

ES6

function fn(...args) {}

Class

ES6

语法糖,本质仍是原型继承

Promise

ES6

异步流程控制

Symbol

ES6

唯一标识符

Set / Map

ES6

新集合类型

async/await

ES8

Promise 语法糖

Object.values / Object.entries

ES8

对象遍历

Array.flat / flatMap

ES10

数组扁平化

Object.fromEntries

ES10

entries → 对象

可选链 ?.

ES2020

安全访问深层属性

空值合并 ??

ES2020

仅对 null/undefined 生效

逻辑赋值 ??=||=&&=

ES2021

条件赋值简写

at()

ES2022

负索引访问(arr.at(-1)

私有字段 #

ES2022

this.#count


十、学习路线与推荐资源

学习路线图

阶段一:基础语法(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)