本工具使用的开源库

本工具代码中捆绑了 1 个开源库。

JavaScript 速查 — 简明参考

现代 JavaScript (ES2022) 语法、标准库与最常用 DOM / Node 帮助函数的速查手册,覆盖约 80% 日常场景。

JS

JavaScript ES2022

ECMAScript · 多范式 · 动态 · 弱类型

学习路径

先跑通「Hello World 与运行环境」(Node.js 或浏览器控制台)→ 熟悉变量、类型与流程控制 → 掌握数组与对象的高阶方法 → 理解引用与拷贝、函数与闭包 → 重点学习异步(Promise/async/await)→ 最后按需查 DOM、网络 fetch、Node 模块与构建调试。FAQ 节适合回头避坑。

1.Hello World 与运行环境

用 Node.js 或浏览器运行 JavaScript,理解脚本、模块与 npm 项目结构。

console 输出

console.log 输出到控制台。console.error 错误输出、console.table 表格展示。浏览器与 Node 通用。

1
2
3
4
5
console.log('Hello, world!');
console.error('something failed'); // 红色错误输出
console.log('values:', 42, true); // 多参数
console.table([{ id: 1 }, { id: 2 }]); // 表格
// Node:$ node script.js

Node 运行

node 直接运行 .js 文件。node -e 执行单行表达式、node -p 打印结果。无浏览器依赖时首选。

1
2
3
4
5
// $ node script.js # 运行脚本
// $ node -e "console.log(1)" # 直接执行
// $ node -p 1 + 2 # 打印结果:3
// 交互式 REPL:$ node
// 语法检查:$ node --check script.js

浏览器 script

HTML 用 <script> 加载 JS。defer 延迟执行(DOM 就绪后)、async 异步加载。module 类型启用 ESM。

1
2
3
4
5
6
<!-- 普通脚本(阻塞渲染) -->
<script src="app.js"></script>
<!-- 延迟执行:DOM 解析完后运行 -->
<script src="app.js" defer></script>
<!-- ES 模块 -->
<script type="module" src="app.js"></script>

命令行参数

Node 中 process.argv 含完整命令行:argv[0] 是 node、argv[1] 是脚本路径、argv[2] 起为参数。

1
2
3
4
5
6
// $ node app.js --name Nick
const args = process.argv.slice(2);
console.log(args); // ['--name', 'Nick']
// 简化解析:
const name = process.argv[process.argv.indexOf('--name') + 1];
console.log('Hello, ' + name);

npm 初始化

npm init 创建 package.json 项目。npm install 安装依赖并生成 node_modules 与 lock 文件。

1
2
3
4
5
// $ npm init -y # 快速初始化
// $ npm install express # 安装依赖并写入 dependencies
// $ npm install -D vitest # 开发依赖
// $ npm run build # 运行 scripts.build
// $ npm ls # 查看已装依赖

package.json

package.json 描述项目:name/version、main 入口、scripts 命令、dependencies 依赖。

1
2
3
4
5
6
7
8
9
10
11
{
"name": "my-app",
"version": "1.0.0",
"type": "module", // ESM 模块
"main": "src/index.js",
"scripts": {
"start": "node src/index.js",
"test": "vitest run"
},
"dependencies": { }
}

ES Modules

import/export 模块化:export 导出、import 导入。package.json 设 type: module 或用 .mjs 扩展名。

1
2
3
4
5
6
7
// math.js
export const PI = 3.14;
export function add(a, b) { return a + b; }
export default function main() { }
// main.js
import main, { add, PI as pi } from './math.js';
console.log(add(1, 2), pi);

严格模式

use strict 开启严格模式:禁止隐式全局变量、静默错误变异常。ESM 模块自动严格模式。

1
2
3
4
5
6
'use strict';
// 禁止未声明就赋值:
x = 10; // ReferenceError
// 严格模式修正历史遗留怪癖
// ESM / class / 现代工具默认已启用
// 建议:代码文件顶部声明

2.变量与常量

let/const 声明、作用域、动态类型、解构与模板字符串。

let 与 const

let 声明可变变量、const 声明常量(对象本身可变)。块级作用域。优先 const,需要改再 let。

1
2
3
4
5
6
7
8
let count = 42;
count = 43; // OK:let 可重新赋值
const name = 'Nick';
// name = 'X'; // 报错:const 不可重新赋值
const obj = { a: 1 };
obj.a = 2; // OK:对象属性可变
const arr = [];
arr.push(1); // OK:数组可修改

var 与 let 区别

var 函数级作用域 + 提升,let/const 块级作用域。现代代码一律 let/const,避免 var 的坑。

1
2
3
4
5
6
7
8
9
if (true) {
var x = 1; // var 提升到函数级
let y = 2; // let 仅块内
}
console.log(x); // 1
// console.log(y); // ReferenceError
// var 可重复声明、有提升:
console.log(z); // undefined(提升)
var z = 3;

作用域

块级作用域 {}、函数作用域、全局作用域。内层可读外层变量,外层读不到内层。

1
2
3
4
5
6
7
8
9
10
let global = 'g';
function outer() {
let outerVar = 'o';
function inner() {
let innerVar = 'i';
console.log(outerVar); // 可访问外层
console.log(global);
}
// innerVar 在此不可见
}

动态类型

JS 变量无类型约束:同一变量可赋任意类型值。typeof 运行时检查当前类型。

1
2
3
4
5
let value = 42; // number
value = 'hello'; // string(允许)
value = { a: 1 }; // object
console.log(typeof value); // 'object'
// 弱类型:'5' + 3 = '53'、'5' - 3 = 2(隐式转换)

解构赋值

从数组/对象提取值到变量。对象解构按名、数组按序。可设置默认值、嵌套解构。

1
2
3
4
5
6
7
const { name, age = 0 } = person; // 对象解构
const [first, , third] = arr; // 数组按序
const { config: { host = 'localhost' } } = app;
// 重命名:
const { name: userName } = person;
// 交换:
[a, b] = [b, a];

命名规范

变量/函数用 lowerCamelCase、常量 UPPER_SNAKE_CASE、类/组件 UpperCamelCase。布尔变量 is/has 前缀。

1
2
3
4
5
6
7
let userId = 1;
const MAX_RETRY = 3;
class UserProfile { }
function fetchData() { }
let isValid = false; // 布尔前缀
let hasPermission = true;
// 文件常用 kebab-case:user-profile.js

提升

var 声明与函数声明提升到作用域顶部。let/const 有暂存死区(TDZ),声明前访问报错。

1
2
3
4
5
6
7
console.log(foo()); // 函数声明提升,可用
function foo() { return 1; }
// console.log(a); // TDZ 报错
let a = 1;
// var 提升到 undefined:
console.log(b); // undefined
var b = 2;

模板字符串

反引号字符串支持 ${} 插值与多行。拼字符串、多行文本首选,避免 + 拼接。

1
2
3
4
5
6
7
8
const name = 'Nick';
const age = 30;
const msg = `${name} is ${age} years old`;
const multi = `第一行
第二行
缩进保留`;
const expr = `${1 + 2}`; // '3'
// 带标签模板:tag`...` 高级用法

3.数据类型

原始类型、对象、typeof 判断与类型转换。

原始类型

7 种原始类型:string、number、boolean、null、undefined、symbol、bigint。其余都是 object。

1
2
3
4
5
6
7
8
const s = 'text'; // string
const n = 42; // number
const b = true; // boolean
const nl = null; // null
const u = undefined; // undefined
const sym = Symbol('id'); // symbol
const big = 10n; // bigint
console.log(typeof s, typeof n, typeof nl);

数字与 NaN

number 覆盖整数与浮点。NaN 表示非数字、Infinity 无穷。NaN 与任何值(含自身)都不相等。

1
2
3
4
5
6
7
const n = 3.14;
const int = 42;
Number.isNaN(NaN); // true
Number.isFinite(1 / 0); // false(Infinity)
parseInt('42px', 10); // 42
parseFloat('3.14'); // 3.14
// 浮点不精确:0.1 + 0.2 !== 0.3

字符串

字符串不可变。长度 length、索引 [i]、方法 toUpperCase/slice/split。字符编码 UTF-16。

1
2
3
4
5
6
7
8
const s = 'hello world';
s.length; // 11
s[0]; // 'h'
s.toUpperCase(); // 'HELLO WORLD'
s.slice(0, 5); // 'hello'
s.split(' '); // ['hello', 'world']
s.includes('lo'); // true
s.startsWith('he'); // true

真值与假值

假值:false、0、''、null、undefined、NaN。其余都是真值。if 判断走真值转换。

1
2
3
4
5
6
7
8
9
// 假值只有 6 个:
!!false === false;
!!0 === false;
!!'' === false;
!!null === false;
!!undefined === false;
!!NaN === false;
// 其余都是真值:[] 真、{} 真、'0' 真
// 空数组是真值!if (arr.length) 才准确

null 与 undefined

undefined 表示未定义/未赋值;null 表示显式的空值。访问不存在的属性返回 undefined。

1
2
3
4
5
6
7
let a; // undefined
let b = null; // 显式空
const obj = {};
console.log(obj.x); // undefined
console.log(obj?.x?.y); // undefined(可选链)
// 判空:value == null 同时覆盖 null 和 undefined
if (a == null) { /* 空 */ }

Symbol 与 BigInt

Symbol 唯一标识(对象私有键);BigInt 任意精度大整数,末尾加 n。typeof 各自识别。

1
2
3
4
5
6
7
const key = Symbol('id');
obj[key] = 1; // 唯一属性键
Symbol('a') === Symbol('a'); // false(唯一)
const big = 12345678901234567890n;
big + 10n; // BigInt 运算
// BigInt 不能与 number 混算:
// 10n + 1 // 报错

typeof 判断

typeof 判断原始类型;数组/日期/null 都返回 'object',需其他方法区分。

1
2
3
4
5
6
7
8
typeof 'a'; // 'string'
typeof 42; // 'number'
typeof true; // 'boolean'
typeof undefined; // 'undefined'
typeof null; // 'object'(历史 bug)
Array.isArray([]); // true
// 对象精确判断:
Object.prototype.toString.call([]); // '[object Array]'

类型转换

显式转换:String()/Number()/Boolean()。隐式转换常出坑:+ 拼接、- 运算。

1
2
3
4
5
6
7
8
9
String(42); // '42'
Number('3.14'); // 3.14
Number('abc'); // NaN
Boolean(''); // false
// 隐式:
'5' + 3; // '53'(+ 拼接)
'5' - 3; // 2(- 转数字)
!!'text'; // true
// 转数字安全:Number(null) 是 0

对象类型

除原始类型外都是对象:普通对象、数组、函数、Date、Map 等。对象是引用类型。

1
2
3
4
5
6
7
const obj = { a: 1, b: 'x' }; // 普通对象
const arr = [1, 2]; // 数组
const fn = () => {}; // 函数
const date = new Date(); // 日期
const map = new Map(); // Map
Object.keys(obj); // ['a', 'b']
// typeof 对它们都是 'object' 或 'function'

4.引用与拷贝

值 vs 引用、浅拷贝深拷贝、数组与对象引用、GC。

值 vs 引用

原始类型按值传递(拷贝);对象/数组/函数按引用传递(共享)。这是 JS 最重要的模型。

1
2
3
4
5
6
7
let a = 5, b = a; // b 是 a 的拷贝
b = 10;
console.log(a); // 5(值拷贝)
const objA = { x: 1 };
const objB = objA; // 引用共享
objB.x = 99;
console.log(objA.x); // 99(引用同一对象)

对象引用

函数传参:对象参数共享同一对象,函数内修改成员影响外部。基本类型不受影响。

1
2
3
4
5
6
7
function mutate(o) { o.x = 99; }
const obj = { x: 1 };
mutate(obj);
console.log(obj.x); // 99(被函数修改)
function bad(o) { o = {}; } // 重新赋值不影响外部
// 需要拷贝时显式做:
// const copy = { ...obj };

浅拷贝

展开运算符 {...obj} / [...arr] 或 Object.assign 做一层拷贝。嵌套对象仍共享引用。

1
2
3
4
5
6
const obj = { a: 1, nested: { b: 2 } };
const copy = { ...obj };
copy.a = 99; // 原对象不受影响
copy.nested.b = 99; // 原对象也变(浅拷贝)
const arrCopy = [...arr];
// Object.assign({}, obj) 等价浅拷贝

深拷贝

深拷贝递归复制所有层级。structuredClone(现代)或递归手写。含函数/循环引用需特殊处理。

1
2
3
4
5
6
const deep = structuredClone(obj); // 原生深拷贝
// 修改嵌套不影响原对象:
deep.nested.b = 1;
console.log(obj.nested.b); // 2
// 老环境:
// JSON.parse(JSON.stringify(obj)) 丢失函数/undefined

数组拷贝

[...arr] 浅拷贝、slice() 拷贝、Array.from 转换。嵌套数组(多维)仍是浅拷贝。

1
2
3
4
5
6
const arr = [1, 2, [3]];
const copy = [...arr]; // 浅拷贝
const copy2 = arr.slice(); // 同上
copy[0] = 99; // 原数组不变
copy[2][0] = 99; // 原数组也变!
// 深拷贝多维:structuredClone(arr)

JSON 序列化拷贝

JSON.stringify/parse 是深拷贝的简单法,但丢失 undefined/函数/Symbol,且不支持循环引用。

1
2
3
4
5
6
const obj = { a: 1, b: { c: 2 }, d: undefined };
const copy = JSON.parse(JSON.stringify(obj));
// copy.d 丢失(undefined 不序列化)
// 循环引用报错:
// const o = {}; o.self = o;
// JSON.stringify(o) // 抛错

垃圾回收

V8 自动回收无引用对象。局部变量函数结束即释放。全局/闭包持有会延长生命周期。

1
2
3
4
5
6
7
function create() {
const big = new Array(1000); // 堆分配
return () => big.length; // 闭包持有 big
}
const fn = create();
// fn 不再使用后可置 null 释放:
// fn = null; 让 GC 回收闭包链

对象相等比较

=== 对对象比较的是引用而非内容,两个内容相同的对象也不相等。按字段比较需手动深比较。

1
2
3
4
5
6
7
8
const a = { x: 1 };
const b = { x: 1 };
a === b; // false(引用不同)
const c = a;
c === a; // true(同一引用)
// 内容比较需手动:
JSON.stringify(a) === JSON.stringify(b); // true
// 或逐字段比较(考虑嵌套用递归/库)

5.流程控制

if/else、循环、switch、三元与逻辑短路。

if / else

if/else 按条件分支,条件走真值转换。else-if 链多分支。

1
2
3
4
5
6
7
8
9
10
const score = 85;
let grade;
if (score >= 90) {
grade = 'A';
} else if (score >= 60) {
grade = 'B';
} else {
grade = 'F';
}
console.log(grade);

三元运算符

cond ? a : b 一行条件赋值。嵌套三元可读性差,复杂逻辑用 if/switch。

1
2
3
4
5
6
const age = 20;
const type = age >= 18 ? 'adult' : 'minor';
// 也可用于返回值:
return ok ? data : null;
// 可链式:
const v = n > 0 ? 'pos' : n < 0 ? 'neg' : 'zero';

for 循环

经典 for 用下标。多数集合场景用 for...of 或数组方法更简洁。

1
2
3
4
5
6
for (let i = 0; i < 10; i++) {
console.log(i); // 0..9
}
for (let i = arr.length - 1; i >= 0; i--) {
// 逆序遍历(需要下标时)
}

for...of

for...of 遍历可迭代对象(数组、字符串、Map/Set、NodeList)。无需下标。

1
2
3
4
5
6
7
8
9
for (const item of items) {
console.log(item);
}
for (const ch of 'abc') { console.log(ch); }
for (const [key, val] of map) {
console.log(key, val);
}
// 需要下标:
// for (const [i, v] of arr.entries())

for...in 对象

for...in 遍历对象可枚举键(含继承)。数组用 for...of,遍历对象键用 Object.keys。

1
2
3
4
5
6
7
const obj = { a: 1, b: 2 };
for (const key in obj) {
console.log(key, obj[key]);
}
// 更安全:
Object.keys(obj).forEach(k => console.log(k));
// 数组别用 for...in(遍历的是索引字符串)

while / do-while

while 先判断后执行;do-while 至少执行一次。循环次数未知的场景用。

1
2
3
4
5
6
7
let i = 0;
while (i < 5) { i++; }
let x = 0;
do {
x++;
} while (x < 3); // 至少执行一次
// 文件流/轮询常用 while (true) + break

switch

switch 用严格相等 === 比较。case 匹配后执行到 break。注意 fall-through(漏 break)。

1
2
3
4
5
6
7
8
9
10
11
12
const day = 3;
switch (day) {
case 1:
console.log('Monday');
break;
case 2:
case 3: // 多值合并
console.log('Tue/Wed');
break;
default:
console.log('other');
}

逻辑短路与可选链

&& || ?? 短路求值。?. 可选链安全访问、?? 空值合并(null/undefined 才兜底)。

1
2
3
4
5
6
7
const name = user?.name ?? 'guest'; // 安全取值+兜底
const ok = value && doSomething(); // 真值才执行
const fallback = a || 'default'; // 假值兜底
// 注意区别:
// a ?? 'd' 只对 null/undefined 兜底
// a || 'd' 对一切假值兜底
// ?. 链式:obj?.a?.b?.c

break 与 continue

continue 跳本轮、break 退出循环、带标签 break/continue 控制嵌套循环。

1
2
3
4
5
6
7
8
9
10
for (let i = 0; i < 10; i++) {
if (i % 2 === 0) continue; // 跳过偶数
if (i > 7) break; // 提前退出
}
outer:
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (j === 2) continue outer; // 跳外层下一轮
}
}

6.函数与闭包

函数声明、箭头函数、参数、闭包、this 与回调。

函数声明与表达式

函数声明会提升、函数表达式不提升。箭头函数是表达式更简洁。三者语法不同。

1
2
3
4
function add(a, b) { return a + b; } // 声明(提升)
const sub = function (a, b) { return a - b; }; // 表达式
const mul = (a, b) => a * b; // 箭头函数
// 调用一致:add(1, 2) / sub(5, 3) / mul(2, 4)

箭头函数

箭头函数:简写、无自己的 this/arguments、不能作构造函数。单表达式隐式返回。

1
2
3
4
5
6
7
8
const square = x => x * x; // 单参数可去括号
const add = (a, b) => a + b;
const nothing = () => console.log('hi');
// 多行体需花括号 + return:
const calc = (a, b) => {
const sum = a + b;
return sum * 2;
};

参数与默认值

函数参数可设默认值、rest 收集剩余参数。arguments 是类数组(箭头函数无)。

1
2
3
4
5
6
7
8
9
10
function greet(name = 'guest') {
return 'Hello, ' + name;
}
greet(); // 'Hello, guest'
function sum(...nums) { // rest 参数
return nums.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3); // 6
// 解构参数:
function f({ a, b }) { return a + b; }

闭包

闭包让函数记住创建时的作用域:内层函数可访问外层变量,即使外层已返回。

1
2
3
4
5
6
7
8
function counter() {
let count = 0; // 闭包捕获
return () => ++count; // 每次调用共享 count
}
const c = counter();
c(); // 1
c(); // 2
// 闭包保留外部变量引用,注意内存占用

this 绑定

this 在调用时决定:对象方法指向对象、普通函数非严格模式指向全局、箭头函数继承外层。

1
2
3
4
5
6
7
8
9
const obj = {
name: 'Nick',
greet() { return 'Hi ' + this.name; }, // 方法
arrow: () => this, // 箭头:继承外层 this
};
obj.greet(); // 'Hi Nick'
// 丢失 this:
const g = obj.greet; // g() 时 this 是全局
// 修复:g.bind(obj) 或箭头包裹 () => obj.greet()

call / apply / bind

call/apply 立即调用并指定 this,bind 返回绑定 this 的新函数。apply 传数组参数。

1
2
3
4
5
6
7
8
function info(a, b) { return this.name + a + b; }
const ctx = { name: 'Nick' };
info.call(ctx, 1, 2); // 逐个传参
info.apply(ctx, [1, 2]); // 数组传参
const bound = info.bind(ctx, 1); // 绑定 this+首参
bound(2);
// 复用数组方法:
Array.prototype.slice.call(arguments)

回调函数

回调是把函数作为参数传给异步操作。现代优先用 Promise/async。回调多层嵌套即回调地狱。

1
2
3
4
5
6
7
8
9
function load(cb) {
setTimeout(() => cb('data'), 1000);
}
load(result => console.log(result));
// 回调地狱:
load(a => load(b => load(c => {
// 嵌套太深难维护
})));
// 用 Promise 改写更清晰

IIFE 立即执行

IIFE (function(){})() 立即执行并隔离作用域。现代用块级作用域 {} 或模块替代。

1
2
3
4
5
6
7
8
9
(function () {
const secret = 'private'; // 外部不可访问
})();
// 现代替代:
{
const scoped = 'block scope';
}
// 箭头 IIFE:
(() => console.log('run once'))();

递归

函数调用自身需基线条件否则栈溢出。尾递归优化在多数 JS 引擎未完全支持。

1
2
3
4
5
6
function factorial(n) {
if (n <= 1) return 1; // 基线条件
return n * factorial(n - 1);
}
factorial(5); // 120
// 深递归注意 RangeError: Maximum call stack

7.字符串处理

字符串方法、模板字符串、正则匹配、拆分与字符处理。

常用方法

常用方法:length 长度、slice/substring 截取、indexOf 查找、includes 包含、replace 替换、split 拆分与 trim 去空白。

1
2
3
4
5
6
7
8
const s = ' Hello, World ';
s.trim(); // 'Hello, World'
s.indexOf('World'); // 8
s.includes('lo'); // true
s.slice(0, 5); // ' Hell'
s.replace('World', 'JS'); // ' Hello, JS '
s.split(','); // [' Hello', ' World ']
'5'.padStart(3, '0'); // '005'

大小写与编码

toUpperCase/toLowerCase 转换大小写。charCodeAt 取码点、fromCharCode 反向。localeCompare 比较。

1
2
3
4
5
6
'hello'.toUpperCase(); // 'HELLO'
'HELLO'.toLowerCase(); // 'hello'
'A'.charCodeAt(0); // 65
String.fromCharCode(65); // 'A'
'a'.localeCompare('b'); // -1(字典序)
// 中文字符注意码点:'中'.codePointAt(0)

Unicode 处理

字符串是 UTF-16 码元序列,Emoji 等增补平面字符占 2 个码元。遍历用 for...of 或 Array.from。

1
2
3
4
5
6
const emoji = '😀';
emoji.length; // 2(码元)
[...emoji].length; // 1(完整字符)
for (const ch of 'ab😀') { console.log(ch); }
// 反转字符串(正确处理 emoji):
const rev = [...'abc😀'].reverse().join('');

模板字符串拼接

模板字符串 ${} 插值与多行最推荐。可嵌套表达式、方法调用。

1
2
3
4
5
6
7
const user = { name: 'Nick', age: 30 };
const msg = `${user.name} 今年 ${user.age} 岁`;
const html = `<div>
<p>${user.name}</p>
</div>`; // 多行
// 条件插值:
const tag = `${user.age >= 18 ? '成年' : '未成年'}`;

正则基础

正则字面量 /pattern/ 或 new RegExp。test 判断、exec 匹配。g 全局、i 忽略大小写。

1
2
3
4
5
6
7
const re = /\d{3}-\d{4}/;
re.test('123-4567'); // true
re.exec('call 123-4567'); // 匹配结果
const m = 'abc123'.match(/(\d+)/);
m[0]; // '123'
m[1]; // '123'(组)
// 全局遍历:'a1b2'.matchAll(/\d/g)

正则替换

replace 用正则替换,$1 引用组。替换回调处理复杂逻辑。replaceAll 替换全部字面量。

1
2
3
4
5
6
'2024-01-01'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$1/$2/$3');
// 脱敏:
'13812345678'.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
// 回调:
'abc'.replace(/\w/g, ch => ch.toUpperCase()); // 'ABC'
// 全部替换:'a-b-c'.replaceAll('-', '+')

拆分与连接

split 按分隔符拆数组、join 合并回字符串。split('') 拆字符。注意空字符串拆分行为。

1
2
3
4
5
6
7
'a,b,c'.split(','); // ['a','b','c']
['a','b','c'].join('-'); // 'a-b-c'
'hello'.split(''); // ['h','e','l','l','o']
// 限制数量:
'a,b,c'.split(',', 2); // ['a','b']
// 按空白拆:
' a b '.trim().split(/\s+/); // ['a','b']

数字转字符串

toString/模板转字符串、toFixed 小数位数、toLocaleString 千分位、padStart 补零。

1
2
3
4
5
6
(255).toString(16); // 'ff'
(3.14159).toFixed(2); // '3.14'
(1234567).toLocaleString('en-US'); // '1,234,567'
(42).toString().padStart(5, '0'); // '00042'
// 安全浮点:
(0.1 + 0.2).toFixed(1); // '0.3'

8.数组与对象

数组高阶方法、Map/Set、对象操作与解构。

数组高阶方法

map 映射、filter 过滤、reduce 归约、find 查找、some/every 判断。不修改原数组。

1
2
3
4
5
6
7
const nums = [1, 2, 3, 4];
nums.map(n => n * 2); // [2, 4, 6, 8]
nums.filter(n => n % 2 === 0); // [2, 4]
nums.reduce((a, b) => a + b, 0); // 10
nums.find(n => n > 2); // 3
nums.some(n => n > 3); // true
nums.every(n => n > 0); // true

Map

Map 任意键映射:get/set/has/delete/size。键可以是对象。迭代保插入顺序。

1
2
3
4
5
6
7
8
9
const m = new Map();
m.set('name', 'Nick');
m.set(obj, 'value'); // 对象作键
m.get('name'); // 'Nick'
m.has('name'); // true
m.delete('name');
m.size; // 1
for (const [k, v] of m) { console.log(k, v); }
// 初始化:new Map([['a',1],['b',2]])

Set

Set 去重集合:add/has/delete/size。数组去重与成员判断首选。

1
2
3
4
5
6
7
8
const set = new Set([1, 2, 2, 3]);
set.size; // 3(自动去重)
set.has(2); // true
set.add(4);
set.delete(1);
// 数组去重:
const uniq = [...new Set(arr)];
// 遍历:for (const v of set) / set.forEach

对象操作

Object.keys/values/entries 遍历。Object.assign 合并。Object.freeze 冻结。in 判断属性。

1
2
3
4
5
6
7
const obj = { a: 1, b: 2 };
Object.keys(obj); // ['a', 'b']
Object.values(obj); // [1, 2]
Object.entries(obj); // [['a',1],['b',2]]
Object.assign({}, obj, { c: 3 }); // 合并
'a' in obj; // true
Object.freeze(obj); // 不可修改

reduce 归约

reduce 把数组规约为单个值:累加、分组、扁平化。初始值参数可选但建议提供。

1
2
3
4
5
6
7
8
9
const nums = [1, 2, 3, 4];
const sum = nums.reduce((acc, n) => acc + n, 0);
// 分组:
const byType = items.reduce((acc, it) => {
(acc[it.type] ||= []).push(it);
return acc;
}, {});
// 扁平化:
[[1], [2, 3]].reduce((a, b) => a.concat(b), []);

排序

sort 默认按字符串排序(坑)。数字排序需比较函数。sort 原地修改数组。

1
2
3
4
5
6
[3, 10, 1].sort(); // [1, 10, 3](字符串序!)
[3, 10, 1].sort((a, b) => a - b); // [1, 3, 10] 升序
[3, 10, 1].sort((a, b) => b - a); // 降序
// 对象按字段排序:
people.sort((a, b) => a.age - b.age);
// 拷贝排序:[...arr].sort(...) 不污染原数组

展开与合并

展开运算符 ... 合并数组/对象、复制、展开参数。Rest 收集与 Spread 展开是镜像。

1
2
3
4
5
6
const a = [1, 2], b = [3, 4];
const merged = [...a, ...b]; // [1,2,3,4]
const copy = [...a]; // 浅拷贝
const obj = { ...o1, ...o2 }; // 对象合并
const max = Math.max(...a); // 展开参数
function f(...rest) { } // 收集参数

方法链式调用

map/filter 等返回新数组可链式。大量操作时注意中间数组分配。

1
2
3
4
5
6
7
const result = people
.filter(p => p.age >= 18)
.map(p => p.name)
.sort()
.join(', ');
// 避免:链中重复大数组操作
// 性能:大数据量可用 for 循环替代

数组空洞与稀疏

数组空洞(hole)与 undefined 不同。map 跳过空洞。密集数组用 Array.from 或 fill。

1
2
3
4
5
6
const holes = [1, , 3]; // 中间是空洞
holes.map(n => n * 2); // [2, <empty>, 6]
// 创建密集数组:
Array.from({ length: 5 }, (_, i) => i); // [0..4]
new Array(5).fill(0); // 密集全 0
// 检查:0 in holes 为 false

9.内存与性能

闭包与内存泄漏、WeakMap/WeakSet、大数据处理与优化。

闭包内存

闭包持有外部变量引用,即使变量不再需要。长期存活引用会阻止 GC。

1
2
3
4
5
6
7
function heavy() {
const big = new Array(1000000);
return function () { return big.length; };
}
const keep = heavy(); // big 被闭包持有
// keep 不再使用应置 null:
// keep = null; 否则 big 无法回收

事件监听器泄漏

DOM 元素被移除但监听器仍引用,或监听器持外部对象导致泄漏。移除元素前解绑或节流。

1
2
3
4
5
6
7
8
9
const btn = document.getElementById('btn');
const handler = () => { /* ... */ };
btn.addEventListener('click', handler);
// 移除前解绑:
// btn.removeEventListener('click', handler);
// 或 btn.onclick = null;
// 使用 AbortController 批量解绑:
// const ac = new AbortController();
// el.addEventListener('click', fn, { signal: ac.signal })

WeakMap / WeakSet

WeakMap 键为弱引用:对象无其他引用即被回收。适合关联数据、缓存、私有字段。

1
2
3
4
5
6
7
8
const wm = new WeakMap();
let obj = {};
wm.set(obj, 'private data'); // 关联但不阻止回收
obj = null; // 键被回收,条目自动消失
// WeakMap 不可迭代、无 size
// 私有字段实现:
// const priv = new WeakMap();
// class A { m() { return priv.get(this); } }

性能优化

避免不必要拷贝与重复计算。局部变量缓存 length、合并 DOM 操作、防抖节流高频事件。

1
2
3
4
5
6
7
8
9
10
11
// 缓存 length:
for (let i = 0, len = arr.length; i < len; i++) { }
// 防抖(高频触发只执行最后一次):
function debounce(fn, ms) {
let t;
return (...args) => {
clearTimeout(t);
t = setTimeout(() => fn(...args), ms);
};
}
const onInput = debounce(save, 300);

栈溢出

无限递归或深递归导致 RangeError: Maximum call stack。改循环或限制深度。

1
2
3
4
5
6
7
8
9
10
// 会栈溢出:
// function loop() { loop(); }
// loop(); // RangeError
// 深树遍历用显式栈:
const stack = [root];
while (stack.length) {
const node = stack.pop();
stack.push(...node.children);
}
// 或用尾递归 / 生成器

大数据处理

大数组 reduce/map 全量分配耗内存。分批处理、生成器惰性、TypedArray 二进制优化。

1
2
3
4
5
6
7
8
9
// 生成器惰性处理:
function* range(n) {
for (let i = 0; i < n; i++) yield i;
}
for (const x of range(1e7)) { /* 按需取 */ }
// 二进制数据:
const buf = new Uint8Array(1024);
// 大文件流式:
// for await (const chunk of stream) { }

GC 与全局对象

全局变量/缓存永不释放。避免无界缓存,及时清理。现代 V8 标记清除自动处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 全局缓存会一直存活:
globalThis.cache = {}; // 永不回收
// 有限缓存:
function lruCache(limit) {
const map = new Map();
return {
get(k) { return map.get(k); },
set(k, v) {
map.set(k, v);
if (map.size > limit) {
map.delete(map.keys().next().value);
}
},
};
}

记忆化缓存

用 Map 缓存纯函数的计算结果,相同入参直接返回缓存,避免重复昂贵计算。注意缓存占用。

1
2
3
4
5
6
7
8
9
10
11
12
function memoize(fn) {
const cache = new Map();
return (...args) => {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const value = fn(...args);
cache.set(key, value);
return value;
};
}
const fib = memoize(n => (n <= 1 ? n : fib(n - 1) + fib(n - 2)));
fib(40); // 秒出(缓存剪枝)

10.面向对象

class 语法、继承、封装、静态成员与原型链。

class 定义

class 定义对象模板:constructor 构造、方法、属性。new 创建实例。

1
2
3
4
5
6
7
8
9
10
11
class Person {
constructor(name, age) {
this.name = name; // 实例属性
this.age = age;
}
greet() { // 实例方法
return 'Hi, ' + this.name;
}
}
const p = new Person('Nick', 30);
p.greet(); // 'Hi, Nick'

继承 extends

extends 继承,super() 调用父类构造、super.method() 调父类方法。

1
2
3
4
5
6
7
8
9
10
11
class Animal {
constructor(name) { this.name = name; }
speak() { return '...'; }
}
class Dog extends Animal {
constructor(name) { super(name); }
speak() { return 'Woof'; } // 重写
}
const d = new Dog('Rex');
d.speak(); // 'Woof'
d.name; // 'Rex'(继承属性)

getter / setter

get/set 定义访问器属性:读写时执行逻辑、加校验。访问如同属性非方法。

1
2
3
4
5
6
7
8
9
10
11
class Account {
constructor() { this._balance = 0; }
get balance() { return this._balance; }
set balance(v) {
if (v < 0) throw new Error('负余额');
this._balance = v;
}
}
const a = new Account();
a.balance = 100; // 触发 setter
console.log(a.balance); // 100(触发 getter)

静态成员

static 成员属于类而非实例:静态方法工具函数、静态属性常量。static {} 静态块初始化。

1
2
3
4
5
6
7
8
class MathUtils {
static PI = 3.14; // 静态属性
static add(a, b) { return a + b; } // 静态方法
static { console.log('init'); } // 静态块
}
MathUtils.add(1, 2); // 通过类调用
MathUtils.PI; // 3.14
// 静态方法中 this 指向类

私有字段

# 前缀定义私有字段/方法:类外访问报错。真实封装(非约定下划线)。

1
2
3
4
5
6
7
8
9
10
class Counter {
#count = 0; // 私有字段
#increment() { // 私有方法
this.#count++;
}
up() { this.#increment(); return this.#count; }
}
const c = new Counter();
c.up(); // 1
// c.#count // 报错:私有字段

instanceof 判断

instanceof 判断对象是否属于某类或其父类链。Object.getPrototypeOf 查看原型。

1
2
3
4
5
6
7
8
class Dog extends Animal { }
const d = new Dog();
d instanceof Dog; // true
d instanceof Animal; // true(父类链)
[] instanceof Array; // true
// 原型链:
Object.getPrototypeOf(d) === Dog.prototype; // true
// 跨 iframe 数组 instanceof 会失败,用 Array.isArray

原型链

对象通过原型链共享方法。class 是原型继承的语法糖。修改 prototype 影响所有实例。

1
2
3
4
5
6
7
8
function Animal(name) { this.name = name; }
Animal.prototype.speak = function () {
return this.name + ' speaks';
};
const a = new Animal('cat');
a.speak();
// 类方法实际挂在 prototype:
Person.prototype.greet === p.greet; // true(方法共享)

组合优于继承

深度继承链难维护。用对象组合:构造时传入依赖、mixin 混合功能。

1
2
3
4
5
6
7
8
9
10
11
class Logger {
constructor(logger) { this.logger = logger; }
save() { this.logger.log('saved'); }
}
// mixin:
const withLogging = (cls) => class extends cls {
log(msg) { console.log(msg); }
};
class User { }
const LoggedUser = withLogging(User);
// 组合:构造注入依赖更易测试

11.异常处理

try/catch/finally、错误类型、抛出、异步错误处理。

try / catch / finally

try 放可能出错的代码,catch 捕获,finally 无论成败都执行。异常沿调用栈传播。

1
2
3
4
5
6
7
8
try {
const data = JSON.parse(json);
} catch (e) {
console.error('parse failed:', e.message);
} finally {
console.log('cleanup'); // 总会执行
}
// catch 省略绑定:catch { ... }

错误类型

Error 基类及其子类:TypeError、ReferenceError、RangeError、SyntaxError。instanceof 区分。

1
2
3
4
5
6
7
8
9
try {
null.prop; // TypeError
undefinedVar; // ReferenceError
} catch (e) {
if (e instanceof TypeError) { }
if (e instanceof ReferenceError) { }
}
// 用户自定义错误通常是 Error 子类
// 异步错误不会进同步 try/catch

抛出错误

throw new Error('msg') 抛异常。可抛任意值但惯例抛 Error 对象(有堆栈)。

1
2
3
4
5
6
7
8
9
10
11
function divide(a, b) {
if (b === 0) {
throw new Error('除数不能为 0');
}
return a / b;
}
try {
divide(1, 0);
} catch (e) {
console.log(e.message); // '除数不能为 0'
}

自定义错误

extends Error 定义业务错误。设置 name 属性便于识别,保留 message 与堆栈。

1
2
3
4
5
6
7
8
9
10
11
12
13
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
}
}
try {
throw new ValidationError('邮箱格式错误');
} catch (e) {
if (e instanceof ValidationError) {
// 业务错误分支
}
}

异步错误

Promise/async 的错误走 .catch 或 try/catch(await 内)。同步 try/catch 捕不到异步抛错。

1
2
3
4
5
6
7
8
9
10
11
12
13
async function load() {
try {
const res = await fetch(url); // 异步
return await res.json();
} catch (e) {
console.error('load failed', e);
return null;
}
}
// 或 Promise 风格:
fetch(url)
.then(r => r.json())
.catch(e => console.error(e));

未处理错误

Promise 无 catch 导致 unhandledrejection。顶层全局监听 uncaughtException/unhandledrejection。

1
2
3
4
5
6
7
8
9
10
// Node 全局:
process.on('uncaughtException', e => {
console.error('uncaught', e);
});
process.on('unhandledRejection', reason => {
console.error('unhandled rejection', reason);
});
// 浏览器:
window.addEventListener('unhandledrejection', e => { });
// 正常开发:每个 async 都 try/catch 或链 .catch

错误隔离

批量处理时单个失败不应中断整体。逐项 try/catch 或 catch 后记录继续。

1
2
3
4
5
6
7
8
9
10
11
const results = [];
for (const item of items) {
try {
results.push(process(item));
} catch (e) {
console.error('skip item:', item, e);
results.push(null); // 标记失败继续
}
}
// 不用裸 catch 吞错误:至少记录
// Promise 批量用 Promise.allSettled 隔离失败

错误原因 cause

ES2022 新增 Error cause 选项,包装错误时保留底层原因与堆栈,便于逐层排查。

1
2
3
4
5
6
7
8
9
10
11
12
13
function parse(data) {
try {
return JSON.parse(data);
} catch (e) {
throw new Error('解析失败', { cause: e });
}
}
try {
parse('not-json');
} catch (e) {
console.log(e.message); // '解析失败'
console.log(e.cause); // 原始解析错误
}

12.文件与 IO

Node 文件系统、控制台输入、JSON 读写与浏览器 DOM IO。

fs 读取文件

Node fs/promises 读取文件:readFile 全读、readFileSync 同步版。现代优先 Promise 版。

1
2
3
4
5
6
7
import { readFile, writeFile } from 'node:fs/promises';
const data = await readFile('in.txt', 'utf8');
await writeFile('out.txt', data);
// 同步版(简单脚本用):
import fs from 'node:fs';
const s = fs.readFileSync('in.txt', 'utf8');
// 逐行:data.split('\n')

fs 写入与追加

writeFile 覆盖写入、appendFile 追加、mkdir 建目录、exists 判断。rename/cp/rm 文件操作。

1
2
3
4
5
6
7
import { writeFile, appendFile, mkdir } from 'node:fs/promises';
await writeFile('a.txt', 'content');
await appendFile('a.txt', '\nmore');
await mkdir('data', { recursive: true });
// 检查存在:
import { access } from 'node:fs/promises';
try { await access('a.txt'); } catch { /* 不存在 */ }

JSON 文件读写

读 JSON 文件 parse、写对象 stringify。JSON 存配置与数据常用。

1
2
3
4
5
6
7
import { readFile, writeFile } from 'node:fs/promises';
const data = JSON.parse(await readFile('config.json', 'utf8'));
data.updated = true;
await writeFile('config.json', JSON.stringify(data, null, 2));
// 容错解析:
try { JSON.parse(s) } catch { /* 处理坏 JSON */ }
// 优雅:JSON.stringify(obj, null, 2) 缩进美化

标准输入输出

process.stdin 读输入(流)、readline 逐行交互、process.stdout.write 输出。

1
2
3
4
5
6
7
8
9
10
11
import readline from 'node:readline';
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question('你的名字?', name => {
console.log('你好', name);
rl.close();
});
// 逐行处理管道输入:
// for await (const line of rl) { }

fetch 网络 IO

fetch 读取远程资源。async/await 处理响应,res.json()/res.text() 解析。Node 18+ 内置。

1
2
3
4
5
6
7
8
9
const res = await fetch('https://api.example.com/data');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
// 写请求:
await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Nick' }),
});

浏览器 DOM IO

DOM 读写页面内容:getElementById 查找、textContent 文本、innerHTML 标记。仅浏览器环境。

1
2
3
4
5
6
7
const el = document.getElementById('output');
el.textContent = 'Hello'; // 安全文本
// el.innerHTML = '<p>Hi</p>'; // HTML(注意 XSS)
const input = document.querySelector('input');
const val = input.value; // 读输入
// 监听事件:
btn.addEventListener('click', () => { });

流式 IO

大文件用流避免整块读入内存。createReadStream/createWriteStream、pipe 或 for await。

1
2
3
4
5
6
7
8
9
import { createReadStream } from 'node:fs';
const stream = createReadStream('big.log', 'utf8');
for await (const chunk of stream) {
processChunk(chunk); // 分块处理
}
// 管道:
// createReadStream('a').pipe(createWriteStream('b'))
// 浏览器响应流:
// for await (const c of res.body)

path 路径处理

node:path 处理文件路径:join 拼接、resolve 转绝对路径、extname 扩展名、basename 文件名。

1
2
3
4
5
6
7
import path from 'node:path';
path.join('data', 'sub', 'a.txt'); // 'data/sub/a.txt'(跨平台)
path.resolve('data'); // 绝对路径
path.extname('a/b.json'); // '.json'
path.basename('/a/b/file.txt'); // 'file.txt'
path.parse('/a/b/file.txt'); // 各片段对象
// 相对:path.relative('/a', '/a/b') // 'b'

13.常见陷阱(FAQ)

JS 开发者最常踩的坑:== vs ===、this、异步、拷贝与真值。

== 与 ===

== 做隐式转换(易错),=== 严格相等。一律用 ===。除 null == undefined 场景。

1
2
3
4
5
6
7
8
9
// BAD:隐式转换出意外
1 == '1'; // true
null == 0; // false
[] == false; // true(诡异)
// GOOD:严格相等
1 === '1'; // false
// 判空:value === null || value === undefined
// 或 value == null(等价)

this 丢失

方法赋值/回调中 this 不再指向对象。箭头函数继承外层、bind 显式绑定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// BAD:回调中 this 丢失
class A {
init() {
btn.onclick = function () {
this.doSomething(); // this 是按钮!
};
}
}
// GOOD:箭头函数
class B {
init() {
btn.onclick = () => this.doSomething(); // this 指向实例
}
}

浮点精度

0.1 + 0.2 !== 0.3(二进制表示不精确)。比较用容差,金额用整数分/库。

1
2
3
4
5
6
7
// BAD:浮点直接相等
0.1 + 0.2 === 0.3; // false
// GOOD:容差比较
Math.abs((0.1 + 0.2) - 0.3) < 1e-9; // true
// 金额:用分存整数 1990 而非 19.9
// 或 toFixed 展示:(0.1+0.2).toFixed(2) === '0.30'

异步执行顺序

setTimeout/网络请求是异步,代码按同步顺序写完不保证按顺序执行。用 await/Promise。

1
2
3
4
5
6
7
8
9
// BAD:以为同步执行
let data;
fetch(url).then(r => data = r);
console.log(data); // undefined!
// GOOD:await 等待
const res = await fetch(url);
console.log(res); // 数据就绪
// 或用 .then 链 / async 函数

浅拷贝深拷贝

展开运算符只拷贝一层,嵌套对象仍共享。改嵌套会影响原对象。

1
2
3
4
5
6
7
8
// BAD:展开以为深拷贝
const b = { ...a };
b.nested.x = 1; // a.nested.x 也变了
// GOOD:深拷贝
const c = structuredClone(a);
c.nested.x = 1; // a 不受影响
// 无 structuredClone 环境用 JSON 或手写递归

真值判断

空数组/空对象是真值。判断数组空要用 .length、对象空用 Object.keys().length。

1
2
3
4
5
6
7
// BAD:if (arr) 恒真
if ([]) { console.log('runs'); } // 空数组是真值
// GOOD:检查长度
if (arr.length > 0) { }
if (Object.keys(obj).length === 0) { /* 空对象 */ }
// 字符串:if (str) 检查非空字符串是安全的

var 作用域泄漏

var 无块级作用域,循环/if 里声明会泄漏到函数级。循环变量用 let。

1
2
3
4
5
6
7
8
9
// BAD:var 泄漏 + 闭包捕获旧值
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i)); // 全打印 3
}
// GOOD:let 块级作用域
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i)); // 0, 1, 2
}

parseInt 陷阱

parseInt('08') 老引擎按八进制?parseInt('3px') 返回 3。严格解析用 Number 或 +。

1
2
3
4
5
6
7
8
9
// BAD:parseInt 宽松解析
parseInt('42px'); // 42(可能非预期)
// 老引擎八进制:
// parseInt('010') 可能是 8
// GOOD:显式进制 + 严格
parseInt('010', 10); // 10
Number('42px'); // NaN(严格)
+'42'; // 42

嵌套三元

多层三元难以阅读。复杂分支用 if/else 或对象映射。

1
2
3
4
5
6
7
8
9
10
// BAD:嵌套三元难读
const v = a ? b ? 'x' : 'y' : 'z';
// GOOD:if/else 或映射
let v;
if (a) v = b ? 'x' : 'y';
else v = 'z';
// 或映射表:
const map = { x: 'X', y: 'Y' };
const result = map[key] ?? 'default';

对象属性变化

对象解构是浅拷贝,嵌套属性仍是引用。防止改原对象需深拷贝。

1
2
3
4
5
6
7
8
// BAD:解构后改嵌套
const { nested } = obj;
nested.x = 99; // obj.nested.x 也变
// GOOD:深拷贝后再改
const copy = structuredClone(obj);
copy.nested.x = 99; // 原对象不变
// 或避免共享:copy 前解构完整值

14.异步与事件循环

事件循环、Promise、async/await、定时器与并发。

事件循环

JS 单线程 + 事件循环。同步代码先执行,异步回调(定时器/IO)进任务队列待处理。

1
2
3
4
5
console.log('1'); // 同步
setTimeout(() => console.log('2')); // 宏任务
Promise.resolve().then(() => console.log('3')); // 微任务
// 输出顺序:1, 3, 2
// 微任务先于宏任务;IO 完成后回调入队

Promise

Promise 表示异步结果。then 处理成功、catch 处理失败、finally 收尾。链式避免回调地狱。

1
2
3
4
5
6
7
8
const p = new Promise((resolve, reject) => {
setTimeout(() => resolve('data'), 1000);
});
p.then(data => console.log(data))
.catch(err => console.error(err))
.finally(() => console.log('done'));
// resolve 成功、reject 失败
// then 返回新 Promise 可继续链

async / await

async 函数返回 Promise,await 等待结果并像同步书写。try/catch 捕获异步错误。

1
2
3
4
5
6
7
8
9
10
11
12
async function load() {
try {
const res = await fetch(url); // 等待
return await res.json();
} catch (e) {
console.error(e);
return null;
}
}
const data = await load();
// await 只能在 async 函数或顶层模块
// async 函数总是返回 Promise

定时器

setTimeout 延迟执行、setInterval 周期执行、clearTimeout/clearInterval 取消。延迟单位毫秒。

1
2
3
4
5
6
7
const t = setTimeout(() => console.log('once'), 1000);
clearTimeout(t); // 取消
const id = setInterval(() => tick(), 500);
clearInterval(id); // 停止
// 延迟 0 也异步:
setTimeout(() => console.log('async'), 0);
// Node:setImmediate 宏任务、process.nextTick 微任务

Promise 组合

Promise.all 全部成功才成功;allSettled 全部结束后汇报各自状态;race 先完成者胜。

1
2
3
4
5
6
7
8
const p1 = fetch('/a');
const p2 = fetch('/b');
// 并行等待全部(任一失败即失败):
const [a, b] = await Promise.all([p1, p2]);
// 不因单个失败中断:
const results = await Promise.allSettled([p1, p2]);
// results[i].status === 'fulfilled' | 'rejected'
// 竞速:Promise.race([p, timeout])

Web Worker

Worker 在独立线程跑耗时代码,不阻塞 UI。postMessage 通信。Node 用 worker_threads。

1
2
3
4
5
6
7
8
9
10
11
// 主线程:
const w = new Worker('worker.js');
w.postMessage('start');
w.onmessage = e => console.log('result', e.data);
// worker.js:
onmessage = e => {
const result = heavyCompute(e.data);
postMessage(result);
};
// Node:
// import { Worker } from 'node:worker_threads'

并发控制

批量异步任务限制并发数,避免资源耗尽。分批或信号量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async function mapLimit(items, limit, fn) {
const results = [];
const running = [];
for (const item of items) {
const p = fn(item).then(r => {
results.push(r);
running.splice(running.indexOf(p), 1);
});
running.push(p);
if (running.length >= limit) {
await Promise.race(running);
}
}
await Promise.all(running);
return results;
}
// 分批:for (let i = 0; i < items.length; i += limit)

微任务与宏任务

微任务(Promise.then、queueMicrotask)优先于宏任务(定时器)。同一轮内微任务先清空。

1
2
3
4
5
queueMicrotask(() => console.log('micro'));
setTimeout(() => console.log('macro'), 0);
// 输出:micro, macro
// 顺序:同步 → 微任务清空 → 宏任务
// 每完成一个宏任务都检查清空微任务队列

15.网络与 HTTP

fetch 请求、HTTP 方法、错误处理、JSON、WebSocket 与 URL。

fetch 基础

fetch 发 HTTP 请求返回 Promise。res.ok 判断成功、res.json()/text() 读取。Node 18+ 与浏览器内置。

1
2
3
4
5
6
const res = await fetch('https://api.example.com/users');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const users = await res.json();
// 或文本:await res.text()
// 状态码:res.status(200/404...)
// 头:res.headers.get('content-type')

POST 与请求体

fetch 第二个参数配置 method、headers、body。JSON 请求体需 JSON.stringify。

1
2
3
4
5
6
7
8
9
10
const res = await fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token',
},
body: JSON.stringify({ name: 'Nick', age: 30 }),
});
if (!res.ok) throw new Error('create failed');
const created = await res.json();

查询参数

URLSearchParams 构建查询串。encodeURIComponent 编码特殊字符。URL 解析。

1
2
3
4
5
6
7
8
9
10
11
12
const params = new URLSearchParams({
q: '速查', page: '2',
});
const url = `/search?${params}`; // q=%E9%80%9F%E6%9F%A5&page=2
// 追加:
params.append('sort', 'asc');
params.get('q'); // '速查'
// 编码单值:
const q = encodeURIComponent('a&b');
// 解析 URL:
const u = new URL('https://a.com/x?y=1');
u.searchParams.get('y'); // '1'

网络错误处理

fetch 只在网络层失败才 reject(断网),HTTP 4xx/5xx 不抛错需检查 res.ok。超时用 AbortController。

1
2
3
4
5
6
7
8
9
10
11
12
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 10000);
try {
const res = await fetch(url, { signal: controller.signal });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
} catch (e) {
if (e.name === 'AbortError') console.error('超时');
else console.error('网络错误', e);
} finally {
clearTimeout(timer);
}

WebSocket

WebSocket 全双工长连接。onopen/onmessage/onclose 事件,send 发送。实时推送场景。

1
2
3
4
5
6
7
const ws = new WebSocket('wss://example.com/ws');
ws.onopen = () => ws.send('hello');
ws.onmessage = e => console.log('收到', e.data);
ws.onclose = () => console.log('连接关闭');
ws.onerror = e => console.error('错误', e);
// 关闭:ws.close()
// 心跳保活:定时 ping 判断连接健康

axios 对比

axios 提供拦截器、自动 JSON、错误对象更友好。fetch 是原生,axios 是第三方库。

1
2
3
4
5
6
7
8
9
10
11
import axios from 'axios';
// GET:
const { data } = await axios.get('/api/users');
// POST:
await axios.post('/api/users', { name: 'Nick' });
// 拦截器:
axios.interceptors.request.use(cfg => {
cfg.headers.Authorization = 'Bearer ' + token;
return cfg;
});
// 错误:err.response.status / err.response.data

HTTP 状态码

2xx 成功、3xx 重定向、4xx 客户端错误、5xx 服务端错误。常见 200/201/400/401/404/500。

1
2
3
4
5
6
7
8
9
10
11
const map = {
200: 'OK 成功',
201: 'Created 已创建',
204: 'No Content 无内容',
400: 'Bad Request 参数错误',
401: 'Unauthorized 未认证',
403: 'Forbidden 无权限',
404: 'Not Found 未找到',
500: 'Internal Server Error 服务器错误',
};
console.log(map[res.status] ?? '其他');

文件上传 FormData

FormData 构造 multipart 表单,fetch POST 上传文件与字段。进度监听用 XMLHttpRequest 更方便。

1
2
3
4
5
6
7
8
9
10
11
const file = document.querySelector('input[type=file]').files[0];
const form = new FormData();
form.append('file', file);
form.append('desc', '示例文件');
await fetch('/api/upload', {
method: 'POST',
body: form, // 自动 multipart,勿手动设 Content-Type
});
// 进度:
// xhr.upload.onprogress = e => pct = e.loaded / e.total
// 多文件:for (const f of files) form.append('file', f)

16.日期与时间

Date、时间戳、格式化、时区与定时器。

Date 基础

new Date() 当前时间、getFullYear/getMonth(0 起)/getDate。month 是 0 基索引。

1
2
3
4
5
6
7
const now = new Date();
now.getFullYear(); // 2024
now.getMonth(); // 0-11(1 月是 0!)
now.getDate(); // 当月几号
now.getDay(); // 星期 0-6(周日起 0)
now.getHours();
now.toISOString(); // UTC ISO 字符串

时间戳

Date.now() 毫秒时间戳(UTC epoch)。getTime() 同。跨时区存储用时间戳或 ISO。

1
2
3
4
5
6
7
Date.now(); // 毫秒时间戳
new Date().getTime(); // 同上
new Date(1700000000000); // 从毫秒构建
// 秒级:Math.floor(Date.now() / 1000)
// 解析字符串:
new Date('2024-01-01T12:00:00Z');
// 解析格式要统一,避免歧义(如 '01/02')

格式化输出

toLocaleDateString/toLocaleString 本地化格式。Intl.DateTimeFormat 精细控制。

1
2
3
4
5
6
7
8
9
const d = new Date();
d.toISOString(); // '2024-01-01T04:00:00.000Z'
d.toLocaleDateString('zh-CN'); // '2024/1/1'
d.toLocaleString('zh-CN', {
year: 'numeric', month: '2-digit', day: '2-digit',
});
// 手拼 yyyy-MM-dd:
const pad = n => String(n).padStart(2, '0');
const s = `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())}`;

时区

getTime 是 UTC 时间点,本地方法(getHours)按运行时区显示。存储用 ISO/时间戳。

1
2
3
4
5
6
7
8
9
const d = new Date();
d.getTimezoneOffset(); // 本地与 UTC 偏移(分钟)
// UTC 方法:
d.getUTCHours();
// 指定时区格式化:
new Intl.DateTimeFormat('zh-CN', {
timeZone: 'Asia/Shanghai',
}).format(d);
// 服务器存 UTC,前端按本地显示

时间差

两个 Date 相减得毫秒差。换算秒/分/时。性能测量用 performance.now 更精确。

1
2
3
4
5
6
7
8
9
10
const start = Date.now();
doWork();
const ms = Date.now() - start;
// 换算:
const secs = Math.floor(ms / 1000);
const mins = Math.floor(ms / 60000);
// 精确计时(毫秒级高精度):
const t0 = performance.now();
doWork();
const precise = performance.now() - t0;

日期运算

加天数/月用 setDate/setMonth 或毫秒运算。注意月份 0 基与跨月边界。

1
2
3
4
5
6
7
8
9
const d = new Date();
d.setDate(d.getDate() + 7); // 加 7 天
// 加月:
d.setMonth(d.getMonth() + 1);
// 加小时:
const later = new Date(Date.now() + 3 * 3600_000);
// 清理时间部分:
d.setHours(0, 0, 0, 0); // 当天 0 点
// 注意 DST 时区加整天用 setDate 更安全

日期解析

new Date(string) 解析日期字符串。ISO 格式最可靠('2024-01-01T12:00:00Z')。其他格式有兼容性差异。

1
2
3
4
5
6
7
8
9
new Date('2024-01-01'); // 本地时区 0 点
new Date('2024-01-01T12:00:00Z'); // UTC 明确
new Date('2024/01/01'); // 浏览器支持,勿依赖
// 解析校验:
const d = new Date(str);
if (isNaN(d.getTime())) { /* 非法日期 */ }
// 手动解析(可控):
const [y, m, day] = '2024-01-01'.split('-').map(Number);
new Date(y, m - 1, day); // month 0 基

Intl 格式化

Intl.NumberFormat 数字/货币、Intl.DateTimeFormat 日期。按区域本地化输出。

1
2
3
4
5
6
7
8
9
10
const n = 1234567.89;
new Intl.NumberFormat('zh-CN').format(n); // '1,234,567.89'
new Intl.NumberFormat('zh-CN', {
style: 'currency', currency: 'CNY',
}).format(n); // '¥1,234,567.89'
new Intl.DateTimeFormat('zh-CN', {
dateStyle: 'medium', timeStyle: 'short',
}).format(new Date());
// 相对时间:
// new Intl.RelativeTimeFormat('zh-CN').format(-2, 'day')

17.Node 进程

process 环境变量、参数、退出码、当前目录与进程信息。

环境变量

process.env 读取环境变量(Node)。process.env.NODE_ENV 常见。跨平台配 .env 文件。

1
2
3
4
5
6
7
8
process.env.NODE_ENV; // 'production' | 'development'
process.env.PORT;
// 设置:
process.env.MY_VAR = 'value';
// 使用 dotenv:
// import 'dotenv/config';
// 读取 .env 文件的变量
// 生产配置不硬编码密钥

命令行参数

process.argv 原始参数。解析选项可用 commander/yargs 库,简单场景手写。

1
2
3
4
5
6
7
8
9
10
11
// $ node app.js --name Nick
const args = process.argv.slice(2);
// 用 commander:
import { Command } from 'commander';
const program = new Command();
program
.option('-n, --name <name>', '名字')
.parse(process.argv);
console.log(program.opts().name);
// 校验参数缺失:
if (!args.includes('--name')) process.exit(1);

退出码

process.exit(code) 退出并返回码(0 成功、非 0 失败)。未捕获异常默认码 1。

1
2
3
4
5
6
7
8
9
process.exit(0); // 成功
process.exit(1); // 一般错误
// 未捕获异常退出码:
process.on('uncaughtException', e => {
console.error(e);
process.exit(1);
});
// async 里用 process.exitCode 更优雅:
process.exitCode = 1; // 事件循环排空后退出

当前目录与路径

process.cwd() 当前工作目录、__dirname 模块目录、__filename 模块文件(CJS)。

1
2
3
4
5
6
7
process.cwd(); // 启动时的 cwd
// ESM 中:
import { fileURLToPath } from 'node:url';
const __dirname = fileURLToPath(new URL('.', import.meta.url));
// 读配置文件相对 cwd 还是模块目录?
// 依赖运行目录时用 process.cwd()
// 相对脚本本身用 __dirname

进程信号

SIGINT(Ctrl+C)、SIGTERM(kill 默认)信号处理,优雅关闭(存数据、断连接)。

1
2
3
4
5
6
7
8
9
10
process.on('SIGINT', () => {
console.log('收到 Ctrl+C,清理中...');
cleanup();
process.exit(0);
});
process.on('SIGTERM', () => {
console.log('收到终止信号,优雅退出');
server.close();
});
// 防止重复触发:用 flag 标记清理中

进程信息

process.pid 进程 ID、process.uptime() 运行秒数、process.memoryUsage() 内存、process.platform 平台。

1
2
3
4
5
6
7
8
process.pid; // 进程 ID
process.platform; // 'win32' | 'linux' | 'darwin'
process.arch; // 'x64'
process.uptime(); // 运行秒数
process.memoryUsage(); // { rss, heapUsed, ... }
process.version; // Node 版本
// 常用调试:
console.log(process.memoryUsage().heapUsed / 1024 / 1024);

标准流

process.stdin/stdout/stderr 三大流。管道数据处理、输出重定向与管道协作。

1
2
3
4
5
6
7
8
9
10
11
// 输出:
process.stdout.write('text'); // 无换行
process.stderr.write('error'); // 错误流
// 逐行读 stdin(管道):
import readline from 'node:readline';
const rl = readline.createInterface({ input: process.stdin });
for await (const line of rl) {
console.log(line.toUpperCase()); // 每行处理
}
// $ cat file.txt | node app.js > out.txt
// 流是否 TTY:process.stdout.isTTY

子进程 child_process

child_process 启动外部命令:exec 取输出、spawn 流式交互、fork 运行 JS 脚本。注意 shell 注入。

1
2
3
4
5
6
7
8
9
10
import { exec, spawn } from 'node:child_process';
// exec:执行命令并取完整输出
exec('ls -la', (err, stdout) => {
if (err) console.error(err.message);
else console.log(stdout);
});
// spawn:流式处理大输出
const child = spawn('node', ['worker.js'], { stdio: 'inherit' });
child.on('exit', code => console.log('退出码', code));
// 优先 execFile/spawn 传数组参数,避免 shell 注入

18.正则表达式

正则字面量、匹配、捕获组、替换与常用模式。

正则语法

字面量 /pattern/ 与 new RegExp。\d 数字、\w 单词字符、\s 空白、[] 字符类、{} 量词。

1
2
3
4
5
6
7
const email = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
const phone = /^1[3-9]\d{9}$/;
/\d{4}-\d{2}-\d{2}/; // 日期
/colou?r/; // color/colour
/\bword\b/; // 词边界
// 字符串转正则需要双反斜杠:
new RegExp('\\d+'); // 等于 /\d+/

正则标志

g 全局、i 忽略大小写、m 多行(^$ 匹配行)、s dotAll(. 匹配换行)、u unicode。

1
2
3
4
5
6
7
const g = /\b\w+\b/g; // 全局匹配
const i = /hello/i; // 忽略大小写
const m = /^start/m; // 每行开头
const s = /a.b/s; // . 也匹配换行
// 内联:
/(?i)hello/; // 忽略大小写
// 组合:/ab/gi

test 与 match

test 返回布尔、match 返回匹配结果、matchAll 遍历全部。exec 单次匹配有 lastIndex。

1
2
3
4
5
6
7
8
const re = /\d+/g;
re.test('abc123'); // true
'abc123'.match(/\d+/); // ['123', index, input]
// 全部匹配:
'a1b2'.matchAll(/\d/g);
// [...str.matchAll(/\d/g)].map(m => m[0]);
// 替换计数:
('a1b2'.match(/\d/g) || []).length; // 2

捕获组

圆括号捕获,match[1] 取组、命名组 (?<name>...) 用 match.groups.name。非捕获组 (?:...)。

1
2
3
4
5
6
7
const m = '2024-01-01'.match(/(\d{4})-(\d{2})-(\d{2})/);
m[1]; // '2024'
m[2]; // '01'
// 命名组:
const n = '2024-01-01'.match(/(?<year>\d{4})-(?<month>\d{2})/);
n.groups.year; // '2024'
// 非捕获:/(?:ab)+/ 只分组不捕获

正则替换

replace 支持 $1/$<name> 组引用、回调函数。replaceAll 替换全部(g 语义)。

1
2
3
4
5
6
'2024-01-01'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$2/$3/$1');
// 脱敏:
'13812345678'.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
// 回调:
'abc'.replace(/\w/g, c => c.toUpperCase()); // 'ABC'
// 全部:'a-b'.replaceAll('-', '+')

常用模式

邮箱、URL、IP、手机号、纯数字/字母。生产校验配合白名单更稳。

1
2
3
4
5
6
7
8
9
const patterns = {
email: /^[^@\s]+@[^@\s]+\.[^@\s]+$/,
url: /^https?:\/\/[^\s]+$/,
ipv4: /^(\d{1,3}\.){3}\d{1,3}$/,
phone: /^1[3-9]\d{9}$/,
alnum: /^[a-zA-Z0-9]+$/,
};
patterns.email.test('[email protected]'); // true
// 注意:简单校验足够,生产用专门库更严

正则性能

避免灾难性回溯(嵌套量词)。长输入用预编译正则。必要时用字符串方法代替。

1
2
3
4
5
6
7
8
9
// 慢:嵌套量词易回溯
/(a+)+$/; // 对 'aaaaaaaaX' 可能很慢
// 优化:
/(?:a+)$/;
// 预编译复用:
const re = /pattern/g; // 多次 match 复用
// 简单判断优先字符串方法:
str.includes('x') 优于 /x/;
// 长文本匹配注意超时/分批

前瞻与后顾

断言只匹配位置不消费字符:前瞻 (?=)、负前瞻 (?!)、后顾 (?<=)。适合密码与边界校验。

1
2
3
4
5
6
7
const re = /foo(?=\d)/; // 后跟数字的 foo
re.test('foo1'); // true
re.test('fooX'); // false
/\d+(?!px)/.test('10em'); // true(10 后不是 px)
/(?<=@)\w+/.exec('[email protected]'); // 匹配 '@' 后的 b
// 密码强度:同时含字母与数字
/^(?=.*[a-z])(?=.*\d).{6,}$/.test('abc123'); // true

19.构建与调试

npm 脚本、打包工具、调试、测试与代码规范。

npm 脚本

package.json scripts 定义常用命令。npm run 执行。&& 串联、pre/post 钩子。

1
2
3
4
5
6
7
8
9
10
11
12
{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"test": "vitest run",
"lint": "eslint . --fix",
"check": "npm run lint && npm run test"
}
}
// $ npm run dev / npm test
// prebuild 会在 build 前自动执行

打包工具

Vite(现代推荐)、webpack、Rollup、esbuild 打包 ES 模块到浏览器可运行代码。

1
2
3
4
5
6
7
8
9
// Vite(基于 esbuild/rollup):
// vite.config.js
import { defineConfig } from 'vite';
export default defineConfig({
build: { outDir: 'dist' },
});
// $ npm create vite@latest my-app
// $ npm run dev # 开发服务器(HMR)
// $ npm run build # 生产打包

调试

console 分级、debugger 断点、Node --inspect 调试器。source map 定位源码。

1
2
3
4
5
6
7
8
9
console.log('info'); // 普通
console.warn('warn'); // 警告
console.error('error'); // 错误
console.debug('debug');
debugger; // 断点(打开 DevTools 时暂停)
// Node 调试:
// $ node --inspect-brk app.js
// 然后 Chrome 打开 chrome://inspect
// 或 VS Code F5 + launch.json

测试

Vitest/Jest 单元测试:describe 分组、test/it 用例、expect 断言。npm test 运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
import { describe, it, expect } from 'vitest';
function add(a, b) { return a + b; }
describe('add', () => {
it('加和', () => {
expect(add(2, 3)).toBe(5);
expect(add(-1, 1)).toBe(0);
});
});
// 异步测试:
it('async', async () => {
await expect(load()).resolves.toBeDefined();
});
// 覆盖率:vitest run --coverage

代码规范 ESLint

ESLint 检查语法与风格,Prettier 统一格式化。pre-commit 钩子自动检查。

1
2
3
4
5
6
7
8
9
10
11
12
// eslint.config.js
import js from '@eslint/js';
export default [
js.configs.recommended,
{ rules: {
'eqeqeq': ['error', 'always'], // 强制 ===
'no-unused-vars': 'warn',
}},
];
// $ npx eslint src --fix
// Prettier:$ npx prettier --write .
// husky + lint-staged 提交前检查

TypeScript 集成

TS 加类型。tsc 编译、ts-node/tsx 直接运行。Vite 原生支持 .ts。

1
2
3
4
5
6
7
8
9
10
11
// $ npm i -D typescript
// tsconfig.json:
{ "compilerOptions": {
"target": "ES2022",
"strict": true,
"module": "ESNext",
"moduleResolution": "bundler"
} }
// $ npx tsc --noEmit # 类型检查
// Vite 里直接 import './app.ts'
// tsx:$ npx tsx script.ts

CI 与部署

CI 自动跑测试与构建。GitHub Actions 等平台 workflow 配置。产物部署到静态托管/容器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# .github/workflows/ci.yml
name: CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm ci
- run: npm run lint
- run: npm test
- run: npm run build
# 部署:上传 dist/ 到静态托管或构建镜像

ESM 与 CJS

两种模块系统:ESM 静态 import 可 tree-shake,CJS 动态 require 运行时加载。type 字段控制默认。

1
2
3
4
5
6
7
8
9
// ESM(.mjs 或 package.json type: module)
import { readFile } from 'node:fs/promises';
export const NAME = 'app';
// CJS(.cjs 或无 type 字段)
const fs = require('node:fs');
module.exports = { NAME: 'app' };
// ESM 可用动态 import 加载 CJS:
const mod = await import('cjs-pkg');
// 打包时优先 ESM 以启用 tree-shaking
关于本速查

本页是 ECMAScript 2022(JavaScript)的自包含速查手册,覆盖语言核心与浏览器/Node 常用 API 在真实项目中约 80% 的常见用法。内容偏向现代惯用法:let/const 块级作用域、箭头函数、模板字符串、解构赋值、展开运算符、Promise 与 async/await、可选链与空值合并。JavaScript 由 Brendan Eich 于 1995 年在 Netscape 发明,如今是唯一一种所有浏览器原生支持的语言,也是 Web 前端、Node.js 服务端与桌面应用的基础。 19 个章节各自聚焦一个主题:基础语法、变量与作用域、类型、引用与值语义、控制流、函数与闭包、字符串、数组与对象、内存与垃圾回收、原型与类、错误处理、输入输出、常见误区、并发(事件循环)、网络(fetch)、时间、进程、正则与构建工具。每个小节都配有「概念介绍 + 可直接复制的代码片段」。 所有代码与文字均在浏览器本地渲染,无任何数据离开你的设备。权威参考见 MDN Web Docs。

版本 2.1.0