《JavaScript 从入门到精通完全手册》
JavaScript 从入门到精通完全手册
📘 本文档是一份全面的 JavaScript 学习指南,涵盖从基础语法到高级概念的完整知识体系,适合初学者系统性学习,也适合有经验的开发者作为参考手册。
目录
-
JavaScript 简介
-
环境搭建与第一个程序
-
基础语法
-
数据类型
-
运算符
-
控制流
-
函数
-
对象
-
数组
-
字符串处理
-
DOM 操作
-
事件处理
-
作用域与闭包
-
原型与继承
-
类(ES6+)
-
异步编程
-
错误处理
-
模块化
-
ES6+ 常用新特性
-
Web API 精选
-
设计模式
-
性能优化
-
最佳实践
-
测试入门
-
现代工具链
1. JavaScript 简介
1.1 什么是 JavaScript
JavaScript 是一种轻量级、解释型、基于原型的脚本语言,最初由 Brendan Eich 于 1995 年为 Netscape 浏览器创建。它是 Web 开发的三层结构之一:
| 层 | 技术 | 作用 |
|---|---|---|
| 结构层 | HTML | 定义页面内容和结构 |
| 表现层 | CSS | 定义页面样式和布局 |
| 行为层 | JavaScript | 定义页面交互和动态行为 |
1.2 发展简史
-
1995 — Brendan Eich 用 10 天创建了 Mocha(后改名 LiveScript,最终定为 JavaScript)
-
1997 — ECMAScript 1(ES1)标准化
-
1999 — ES3 发布,奠定现代 JS 基础
-
2009 — ES5 发布,新增严格模式、JSON、数组方法等
-
2015 — ES6(ES2015)重大更新,引入 let/const、箭头函数、类、模块等
-
2016 至今 — 每年发布新版本,持续演进
1.3 JavaScript 能做什么
-
🖥️ 前端开发 — DOM 操作、交互逻辑、SPA 应用
-
🔧 后端开发 — Node.js 构建服务器、API
-
📱 移动开发 — DCloud React Native、Flutter
-
🎮 游戏开发 — Canvas、WebGL、游戏引擎
-
🤖 桌面应用 — Electron
-
📊 数据可视化 — D3.js、ECharts
-
🤖 机器学习 — TensorFlow.js
2. 环境搭建与第一个程序
2.1 浏览器控制台(最快上手)
打开 Chrome/Firefox → 按 F12 → 点击 Console 标签:
console.log('Hello, JavaScript!');
2.2 HTML 中嵌入 JavaScript
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>我的第一个 JS 程序</title>
</head>
<body>
<h1>JavaScript 入门</h1>
<!-- 内嵌式 -->
<script>
alert('欢迎来到 JavaScript 的世界!');
</script>
<!-- 外部引入(推荐) -->
<script src="app.js"></script>
</body>
</html>
⚠️ 注意:\<script\> 标签放在 \<body\> 底部可以避免阻塞页面渲染。现代也可使用 defer 或 async 属性。
2.3 Node.js 环境
# 安装 Node.js 后,创建 app.js
node app.js
// app.js
console.log('在 Node.js 中运行 JavaScript');
3. 基础语法
3.1 变量声明
// ES5 - var(函数作用域,可重复声明,存在变量提升)
var name = '张三';
// ES6 - let(块级作用域,不可重复声明)
let age = 25;
// ES6 - const(块级作用域,声明常量,不可重新赋值)
const PI = 3.14159;
// const 对于引用类型,内容可修改
const arr = [1, 2, 3];
arr.push(4); // ✅ 允许
// arr = [5, 6]; // ❌ 报错
三种声明方式对比:
| 特性 | var | let | const |
|---|---|---|---|
| 作用域 | 函数作用域 | 块级作用域 | 块级作用域 |
| 变量提升 | ✅ | ❌(暂时性死区) | ❌(暂时性死区) |
| 可重复声明 | ✅ | ❌ | ❌ |
| 必须初始化 | ❌ | ❌ | ✅ |
| 可重新赋值 | ✅ | ✅ | ❌ |
3.2 命名规范
// ✅ 推荐:驼峰命名法
let userName = 'Alice';
let totalPrice = 99.99;
const MAX_COUNT = 100; // 常量用大写+下划线
// ✅ 允许:字母、数字、下划线、美元符号
let $element = document.querySelector('.box');
let _private = '内部变量';
let age2 = 30;
// ❌ 不允许:数字开头、连字符、关键字
// let 2name = '错误';
// let class = '错误';
3.3 注释
// 这是单行注释
/*
* 这是多行注释
* 可以跨越多行
*/
/**
* 文档注释(JSDoc 风格)
* @param {string} name - 用户名称
* @returns {string} 问候语
*/
function greet(name) {
return `你好,${name}!`;
}
3.4 分号
JavaScript 有自动分号插入(ASI)机制,但推荐手动添加分号以避免意外问题。
// 推荐
let x = 5;
console.log(x);
// 某些情况不写分号可能出问题
let a = 1
let b = 2
// 等价于 let a = 1; let b = 2; —— 这里没问题
// 但以 [ ( 开头的行可能被合并解析
let c = a + b
[1, 2, 3].forEach(n => console.log(n)) // 可能报错
4. 数据类型
4.1 基本数据类型(7 种)
// 1. Number — 数字(整数和浮点)
let num = 42;
let price = 19.99;
let infinity = Infinity;
let notANumber = NaN; // 非数值
// 2. String — 字符串
let single = '单引号';
let double = "双引号";
let backtick = `模板字符串 ${num}`; // ES6
// 3. Boolean — 布尔值
let isReady = true;
let isOff = false;
// 4. Undefined — 未定义
let notDefined;
console.log(notDefined); // undefined
// 5. Null — 空值
let empty = null;
// 6. Symbol — 符号(ES6,唯一标识)
let sym1 = Symbol('id');
let sym2 = Symbol('id');
console.log(sym1 === sym2); // false
// 7. BigInt — 大整数(ES2020)
let bigNum = 9007199254740991n;
let anotherBig = BigInt('9007199254740991');
4.2 类型检测
// typeof 运算符
console.log(typeof 42); // 'number'
console.log(typeof 'hello'); // 'string'
console.log(typeof true); // 'boolean'
console.log(typeof undefined); // 'undefined'
console.log(typeof null); // 'object' ⚠️(历史遗留 bug)
console.log(typeof Symbol()); // 'symbol'
console.log(typeof 123n); // 'bigint'
console.log(typeof function(){}); // 'function'
// instanceof 检测引用类型
console.log([] instanceof Array); // true
console.log({} instanceof Object); // true
// 更准确的类型检测
console.log(Object.prototype.toString.call([])); // '[object Array]'
console.log(Object.prototype.toString.call(null)); // '[object Null]'
4.3 类型转换
// --- 转换为字符串 ---
String(123); // '123'
(123).toString(); // '123'
123 + ''; // '123'(隐式)
// --- 转换为数字 ---
Number('123'); // 123
parseInt('123px'); // 123
parseFloat('12.34'); // 12.34
+'123'; // 123(隐式)
// --- 转换为布尔 ---
Boolean(1); // true
Boolean(0); // false
Boolean(''); // false
Boolean('hello'); // true
!!'hello'; // true(双感叹号技巧)
// --- 假值(Falsy Values)---
false, 0, -0, 0n, '', null, undefined, NaN
// 其他所有值都是真值(Truthy)
4.4 相等性比较
// == 宽松相等(会进行类型转换)
console.log(5 == '5'); // true
console.log(null == undefined); // true
console.log(0 == false); // true
// === 严格相等(类型+值都必须相等)—— 推荐使用
console.log(5 === '5'); // false
console.log(null === undefined);// false
console.log(0 === false); // false
// Object.is() — 更精确的相等判断
console.log(Object.is(NaN, NaN)); // true(=== 下为 false)
console.log(Object.is(0, -0)); // false(=== 下为 true)
5. 运算符
5.1 算术运算符
let a = 10, b = 3;
console.log(a + b); // 13 加法
console.log(a - b); // 7 减法
console.log(a * b); // 30 乘法
console.log(a / b); // 3.333... 除法
console.log(a % b); // 1 取余(模运算)
console.log(a ** b); // 1000 指数(ES7)
console.log(a++); // 10 后置自增
console.log(++a); // 12 前置自增
5.2 赋值运算符
let x = 5;
x += 3; // x = x + 3 → 8
x -= 2; // x = x - 2 → 6
x *= 3; // x = x * 3 → 18
x /= 2; // x = x / 2 → 9
x %= 4; // x = x % 4 → 1
x **= 2; // x = x ** 2 → 1
5.3 比较运算符
console.log(5 > 3); // true
console.log(5 >= 5); // true
console.log(3 < 5); // true
console.log(3 <= 2); // false
console.log(5 === '5'); // false
console.log(5 !== '5'); // true
5.4 逻辑运算符
// && — 逻辑与
console.log(true && true); // true
console.log(true && false); // false
// || — 逻辑或
console.log(false || true); // true
console.log(false || false); // false
// ! — 逻辑非
console.log(!true); // false
// 短路求值
let name = '';
let displayName = name || '匿名用户'; // '匿名用户'
let result = true && '返回这个'; // '返回这个'
// 可选链操作符(ES2020)
let user = null;
console.log(user?.address?.city); // undefined(不会报错)
5.5 三元运算符
let age = 18;
let status = age >= 18 ? '成人' : '未成年';
// 可嵌套(但不宜过深)
let grade = 85;
let level = grade >= 90 ? 'A' : grade >= 80 ? 'B' : grade >= 60 ? 'C' : 'D';
5.6 运算符优先级
记不住?使用括号明确优先级,提高可读性。
// 不用括号
let result = 2 + 3 * 4; // 14,不是 20
// 使用括号
let clear = (2 + 3) * 4; // 20,意图明确
6. 控制流
6.1 if-else
let score = 85;
if (score >= 90) {
console.log('优秀');
} else if (score >= 80) {
console.log('良好');
} else if (score >= 60) {
console.log('及格');
} else {
console.log('不及格');
}
6.2 switch
let day = 3;
switch (day) {
case 1:
console.log('周一');
break;
case 2:
console.log('周二');
break;
case 3:
console.log('周三');
break; // 不要忘记 break
default:
console.log('其他');
}
// switch 使用严格相等(===)
6.3 循环
// --- for 循环 ---
for (let i = 0; i < 5; i++) {
console.log(i); // 0, 1, 2, 3, 4
}
// --- while 循环 ---
let count = 0;
while (count < 5) {
console.log(count);
count++;
}
// --- do-while 循环(至少执行一次)---
let num = 0;
do {
console.log(num);
num++;
} while (num < 0); // 条件为假也会执行一次
// --- for...of 遍历可迭代对象 ---
const fruits = ['苹果', '香蕉', '橙子'];
for (const fruit of fruits) {
console.log(fruit);
}
// --- for...in 遍历对象属性 ---
const person = { name: '张三', age: 30, city: '北京' };
for (const key in person) {
console.log(`${key}: ${person[key]}`);
}
6.4 跳转语句
// break — 终止循环
for (let i = 0; i < 10; i++) {
if (i === 5) break;
console.log(i); // 0, 1, 2, 3, 4
}
// continue — 跳过本次迭代
for (let i = 0; i < 5; i++) {
if (i === 2) continue;
console.log(i); // 0, 1, 3, 4
}
// 标签语句(用于跳出嵌套循环)
outerLoop: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (i === 1 && j === 1) break outerLoop;
console.log(`${i},${j}`);
}
}
7. 函数
7.1 函数声明
// 函数声明(存在提升)
function greet(name) {
return `你好,${name}!`;
}
// 函数表达式(不存在提升)
const sayBye = function(name) {
return `再见,${name}!`;
};
// 箭头函数(ES6)
const multiply = (a, b) => a * b;
const square = x => x * x;
const sayHello = () => console.log('Hello');
// 箭头函数的多行写法
const process = (data) => {
const result = data.trim().toLowerCase();
return result;
};
7.2 参数处理
// 默认参数(ES6)
function greet(name = '访客', greeting = '你好') {
return `${greeting},${name}!`;
}
console.log(greet()); // '你好,访客!'
console.log(greet('张三', 'Hi')); // 'Hi,张三!'
// 剩余参数(ES6)
function sum(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
console.log(sum(1, 2, 3, 4)); // 10
// arguments 对象(旧方式,类数组)
function oldStyle() {
console.log(arguments[0]); // 第一个参数
console.log(arguments.length); // 参数个数
}
7.3 箭头函数 vs 普通函数
| 特性 | 普通函数 | 箭头函数 |
|---|---|---|
| this 绑定 | 动态绑定(调用时确定) | 词法绑定(定义时确定) |
| arguments | ✅ | ❌ |
| 构造函数 | ✅(可用 new) | ❌ |
| prototype | ✅ | ❌ |
| 方法中用作回调 | 需手动绑定 this | 自动捕获外层 this |
// this 绑定的关键区别
const obj = {
name: '测试',
normalFunc: function() {
console.log(this.name); // '测试'(this 指向 obj)
},
arrowFunc: () => {
console.log(this.name); // undefined(this 指向外层作用域)
}
};
7.4 立即执行函数(IIFE)
// 经典写法
(function() {
const privateVar = '我是私有的';
console.log('立即执行');
})();
// 箭头函数版本
(() => {
console.log('箭头函数 IIFE');
})();
7.5 函数是"一等公民"
// 函数可以赋值给变量
const fn = function() { return 'hello'; };
// 函数可以作为参数(回调)
function execute(callback) {
callback();
}
execute(() => console.log('回调执行'));
// 函数可以作为返回值(高阶函数)
function createMultiplier(factor) {
return function(number) {
return number * factor;
};
}
const double = createMultiplier(2);
console.log(double(5)); // 10
7.6 call、apply、bind
function introduce(greeting, punctuation) {
console.log(`${greeting},我是${this.name}${punctuation}`);
}
const person = { name: '张三' };
// call — 逐个传参
introduce.call(person, '你好', '!');
// apply — 数组传参
introduce.apply(person, ['嗨', '~']);
// bind — 返回新函数(不立即执行)
const boundFunc = introduce.bind(person, 'Hello');
boundFunc('!'); // 'Hello,我是张三!'
8. 对象
8.1 对象创建
// 对象字面量
const person = {
name: '张三',
age: 30,
greet() { // 方法简写(ES6)
return `我是${this.name}`;
}
};
// new Object()
const obj = new Object();
obj.key = 'value';
// Object.create()
const proto = { shared: '共享属性' };
const child = Object.create(proto);
console.log(child.shared); // '共享属性'
8.2 属性操作
const user = { name: 'Alice', age: 25 };
// 访问属性
console.log(user.name); // 点号访问
console.log(user['age']); // 方括号访问(动态属性名)
// 计算属性名(ES6)
const key = 'favoriteColor';
const data = {
[key]: '蓝色',
[`${key}_dark`]: '深蓝'
};
// 添加属性
user.email = 'alice@example.com';
// 删除属性
delete user.age;
// 检查属性是否存在
console.log('name' in user); // true
console.log(user.hasOwnProperty('name')); // true
8.3 对象遍历
const obj = { a: 1, b: 2, c: 3 };
// for...in(遍历所有可枚举属性,包括原型链)
for (const key in obj) {
if (obj.hasOwnProperty(key)) { // 过滤原型属性
console.log(key, obj[key]);
}
}
// Object.keys() — 返回自身可枚举属性名数组
console.log(Object.keys(obj)); // ['a', 'b', 'c']
// Object.values() — 返回自身可枚举属性值数组
console.log(Object.values(obj)); // [1, 2, 3]
// Object.entries() — 返回键值对数组
console.log(Object.entries(obj)); // [['a',1], ['b',2], ['c',3]]
8.4 对象方法
const target = { a: 1 };
const source = { b: 2, c: 3 };
// Object.assign() — 浅拷贝/合并
const merged = Object.assign({}, target, source);
console.log(merged); // { a: 1, b: 2, c: 3 }
// 展开运算符(ES2018)
const merged2 = { ...target, ...source };
// Object.freeze() — 冻结对象(不可修改)
const frozen = Object.freeze({ x: 10 });
// frozen.x = 20; // 静默失败或报错(严格模式)
// Object.seal() — 密封对象(不可添加/删除,但可修改现有属性)
const sealed = Object.seal({ y: 20 });
8.5 解构赋值
const user = { name: '张三', age: 30, city: '北京' };
// 基本解构
const { name, age } = user;
console.log(name, age); // '张三', 30
// 别名
const { name: userName, age: userAge } = user;
// 默认值
const { country = '中国' } = user;
// 嵌套解构
const response = {
data: {
user: { id: 1, profile: { nickname: '小王' } }
}
};
const { data: { user: { profile: { nickname } } } } = response;
console.log(nickname); // '小王'
// 函数参数解构
function display({ name, age }) {
console.log(`${name},${age}岁`);
}
display(user);
9. 数组
9.1 数组创建
// 字面量
const arr1 = [1, 2, 3];
// Array 构造函数
const arr2 = new Array(3); // 长度为 3 的空数组
const arr3 = Array.of(3); // [3](ES6)
const arr4 = Array.from('hi'); // ['h', 'i'](ES6)
// 生成有初始值的数组
const filled = Array(5).fill(0); // [0, 0, 0, 0, 0]
9.2 常用方法 — 增删改查
const arr = [1, 2, 3];
// 尾部操作
arr.push(4); // 尾部添加 → [1,2,3,4]
arr.pop(); // 尾部删除 → [1,2,3]
// 头部操作
arr.unshift(0); // 头部添加 → [0,1,2,3]
arr.shift(); // 头部删除 → [1,2,3]
// 任意位置
arr.splice(1, 1, 'a', 'b'); // 从索引1删除1个,插入'a','b'
// splice(起始索引, 删除个数, ...插入项)
// 切片(不改变原数组)
const sliced = arr.slice(0, 2); // 从索引0到索引2(不含)
// 查找
arr.indexOf(3); // 索引位置,不存在返回 -1
arr.includes(3); // true/false(ES7)
arr.find(n => n > 1); // 第一个匹配的元素
arr.findIndex(n => n > 1); // 第一个匹配的索引
9.3 迭代方法
const numbers = [1, 2, 3, 4, 5];
// forEach — 遍历执行(无返回值)
numbers.forEach((item, index) => {
console.log(`${index}: ${item}`);
});
// map — 映射转换为新数组
const doubled = numbers.map(n => n * 2); // [2, 4, 6, 8, 10]
// filter — 过滤
const evens = numbers.filter(n => n % 2 === 0); // [2, 4]
// reduce — 归约累积
const sum = numbers.reduce((acc, cur) => acc + cur, 0); // 15
// some — 是否有元素满足条件
console.log(numbers.some(n => n > 4)); // true
// every — 是否所有元素满足条件
console.log(numbers.every(n => n > 0)); // true
// flatMap — 映射并展平(ES2019)
const arr = [1, 2, 3];
const result = arr.flatMap(n => [n, n * 2]);
console.log(result); // [1, 2, 2, 4, 3, 6]
9.4 排序与反转
const fruits = ['香蕉', '苹果', '橙子', '葡萄'];
// sort — 默认按字符串排序
fruits.sort();
console.log(fruits); // ['橙子', '苹果', '葡萄', '香蕉'](按拼音)
// 自定义排序
const nums = [10, 5, 40, 25, 1];
nums.sort((a, b) => a - b); // 升序: [1, 5, 10, 25, 40]
nums.sort((a, b) => b - a); // 降序: [40, 25, 10, 5, 1]
// reverse — 反转
nums.reverse();
// 注意:sort 和 reverse 都会修改原数组
// 先复制再排序
const sorted = [...nums].sort((a, b) => a - b);
9.5 数组展开与合并
const arr1 = [1, 2];
const arr2 = [3, 4];
// 展开运算符
const combined = [...arr1, ...arr2, 5]; // [1, 2, 3, 4, 5]
// concat
const merged = arr1.concat(arr2);
// 多维数组展平
const nested = [1, [2, [3, 4]]];
console.log(nested.flat()); // [1, 2, [3, 4]] — 默认展平一层
console.log(nested.flat(2)); // [1, 2, 3, 4] — 展平两层
console.log(nested.flat(Infinity)); // [1, 2, 3, 4] — 完全展平
10. 字符串处理
10.1 模板字符串(ES6)
const name = '世界';
const greeting = `你好,${name}!`; // 使用反引号
// 多行字符串
const html = `
<div>
<h1>标题</h1>
<p>内容</p>
</div>
`;
// 表达式嵌入
const price = 100;
const tax = 0.13;
const total = `总价:¥${(price * (1 + tax)).toFixed(2)}`;
// 标签模板
function highlight(strings, ...values) {
return strings.reduce((result, str, i) =>
`${result}${str}<em>${values[i] || ''}</em>`, '');
}
const result = highlight`价格是${price},含税${total}`;
10.2 常用方法
const str = ' Hello, JavaScript World! ';
// 大小写转换
str.toUpperCase(); // ' HELLO, JAVASCRIPT WORLD! '
str.toLowerCase(); // ' hello, javascript world! '
// 去除空白
str.trim(); // 'Hello, JavaScript World!'
str.trimStart(); // 去除头部空白
str.trimEnd(); // 去除尾部空白
// 查找
str.indexOf('Java'); // 8
str.lastIndexOf('o'); // 20
str.includes('Script'); // true
str.startsWith('Hello'); // false(有空格)
str.endsWith('!'); // false(有空格)
str.search(/javascript/i); // 8(支持正则)
// 提取子串
str.slice(8, 18); // 'JavaScript'
str.substring(8, 18); // 'JavaScript'(不接受负数)
str.substr(8, 10); // 'JavaScript'(已废弃)
// 替换
str.replace('World', '世界'); // 只替换第一个
str.replace(/o/g, 'O'); // 全局替换(正则)
// 分割与拼接
str.split(' '); // 按空格分割成数组
['Hello', 'World'].join(', '); // 数组拼接成字符串
// 填充
'42'.padStart(5, '0'); // '00042'
'42'.padEnd(5, '*'); // '42***'
// 重复
'Hi'.repeat(3); // 'HiHiHi'
11. DOM 操作
11.1 获取元素
// 通过 ID
const header = document.getElementById('header');
// 通过 CSS 选择器(推荐)
const box = document.querySelector('.box'); // 第一个匹配
const allBoxes = document.querySelectorAll('.box'); // 所有匹配(NodeList)
// 通过类名
const items = document.getElementsByClassName('item'); // HTMLCollection(动态)
const tags = document.getElementsByTagName('div'); // HTMLCollection(动态)
11.2 操作内容
const element = document.querySelector('.content');
// 文本内容
element.textContent = '新的文本内容'; // 纯文本,安全
element.innerText = '可见文本'; // 考虑 CSS 样式
// HTML 内容
element.innerHTML = '<strong>粗体文本</strong>'; // ⚠️ 注意 XSS 风险
// 安全插入 HTML
element.insertAdjacentHTML('beforeend', '<span>安全插入</span>');
// 位置选项:'beforebegin' | 'afterbegin' | 'beforeend' | 'afterend'
11.3 操作属性
const link = document.querySelector('a');
const input = document.querySelector('input');
// 标准属性
link.href = 'https://example.com';
link.id = 'main-link';
link.className = 'btn primary';
// classList API(推荐)
link.classList.add('active');
link.classList.remove('primary');
link.classList.toggle('visible');
link.classList.contains('active'); // true/false
link.classList.replace('old', 'new');
// 通用属性方法
link.setAttribute('data-id', '123');
link.getAttribute('data-id'); // '123'
link.hasAttribute('data-id'); // true
link.removeAttribute('data-id');
// data-* 属性快捷访问
console.log(link.dataset.id); // '123'(对应 data-id)
11.4 操作样式
const box = document.querySelector('.box');
// 内联样式(写入 style 属性)
box.style.backgroundColor = 'red';
box.style.fontSize = '16px';
box.style.cssText = 'color: white; padding: 10px;';
// 获取计算样式(只读)
const styles = getComputedStyle(box);
console.log(styles.width);
console.log(styles.getPropertyValue('--custom-var')); // CSS 变量
// 使用 class 切换样式(推荐)
box.classList.toggle('dark-mode');
11.5 创建与操作节点
// 创建元素
const div = document.createElement('div');
div.textContent = '新创建的元素';
div.className = 'new-box';
// 创建文本节点
const text = document.createTextNode('纯文本');
// 创建文档片段(批量操作优化)
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
const li = document.createElement('li');
li.textContent = `项目 ${i}`;
fragment.appendChild(li);
}
document.querySelector('ul').appendChild(fragment); // 一次性插入
// 插入节点
parent.appendChild(child); // 末尾添加
parent.insertBefore(newNode, ref); // 在 ref 之前插入
parent.prepend(child); // 开头添加(现代 API)
parent.append(child); // 末尾添加(现代 API,支持多参数)
// 替换与移除
oldNode.replaceWith(newNode);
element.remove(); // 现代 API
parent.removeChild(child); // 传统 API
// 克隆
const clone = element.cloneNode(true); // true = 深克隆(含子节点)
11.6 元素尺寸与位置
const el = document.querySelector('.box');
// 尺寸(含 padding,不含 border 和滚动条)
el.clientWidth;
el.clientHeight;
// 尺寸(含 padding + border,不含滚动条)
el.offsetWidth;
el.offsetHeight;
// 内容实际尺寸(含溢出部分)
el.scrollWidth;
el.scrollHeight;
// 相对定位父元素的位置
el.offsetTop;
el.offsetLeft;
// 相对于视口的位置
const rect = el.getBoundingClientRect();
console.log(rect.top, rect.left, rect.bottom, rect.right, rect.width, rect.height);
// 滚动位置
el.scrollTop;
el.scrollLeft;
window.scrollY; // 页面垂直滚动距离
window.scrollX; // 页面水平滚动距离
12. 事件处理
12.1 事件绑定
const button = document.querySelector('button');
// 方式 1:addEventListener(推荐)
button.addEventListener('click', function(event) {
console.log('按钮被点击', event);
});
// 方式 2:on 属性(只能绑定一个处理函数)
button.onclick = function() {
console.log('点击');
};
// 方式 3:HTML 内联(不推荐)
// <button onclick="handleClick()">点击</button>
// 移除事件监听
function handler() { console.log('处理'); }
button.addEventListener('click', handler);
button.removeEventListener('click', handler); // 必须引用同一个函数
12.2 事件对象
element.addEventListener('click', function(event) {
console.log(event.type); // 'click'
console.log(event.target); // 触发事件的元素
console.log(event.currentTarget); // 绑定事件的元素
console.log(event.clientX); // 鼠标 X 坐标
console.log(event.clientY); // 鼠标 Y 坐标
event.preventDefault(); // 阻止默认行为
event.stopPropagation(); // 阻止冒泡
event.stopImmediatePropagation(); // 阻止同一元素上其他监听器
});
12.3 事件委托
// 利用冒泡,在父元素上监听子元素事件
const list = document.querySelector('ul');
list.addEventListener('click', function(event) {
if (event.target.tagName === 'LI') {
console.log('点击了:', event.target.textContent);
}
// 或使用 matches
if (event.target.matches('.item')) {
console.log('点击了 item');
}
});
// 优势:动态添加的子元素自动拥有事件处理能力
12.4 常用事件类型
// 鼠标事件
'click', 'dblclick', 'mousedown', 'mouseup', 'mousemove',
'mouseenter', 'mouseleave', 'mouseover', 'mouseout', 'contextmenu'
// 键盘事件
'keydown', 'keyup', 'keypress'
document.addEventListener('keydown', (e) => {
console.log(e.key, e.code, e.ctrlKey, e.shiftKey);
});
// 表单事件
'input', 'change', 'submit', 'focus', 'blur', 'reset'
// 文档/窗口事件
'DOMContentLoaded', 'load', 'beforeunload', 'resize', 'scroll'
// 触摸事件
'touchstart', 'touchmove', 'touchend', 'touchcancel'
// 自定义事件
const customEvent = new CustomEvent('myEvent', {
detail: { message: '自定义数据' }
});
element.dispatchEvent(customEvent);
element.addEventListener('myEvent', (e) => {
console.log(e.detail.message);
});
12.5 事件循环机制
// 捕获阶段 → 目标阶段 → 冒泡阶段
// addEventListener 默认在冒泡阶段触发
element.addEventListener('click', handler, false); // 冒泡阶段(默认)
element.addEventListener('click', handler, true); // 捕获阶段
// once 选项(执行一次后自动移除)
button.addEventListener('click', handler, { once: true });
// passive 选项(提升滚动性能)
document.addEventListener('touchstart', handler, { passive: true });
13. 作用域与闭包
13.1 作用域类型
// 全局作用域
const globalVar = '全局变量';
function test() {
// 函数作用域
const functionVar = '函数变量';
if (true) {
// 块级作用域(let/const)
const blockVar = '块级变量';
var functionScoped = '仍然是函数作用域';
}
console.log(functionScoped); // ✅ 可访问
// console.log(blockVar); // ❌ 不可访问
}
// 作用域链:内层可访问外层,反之不行
13.2 变量提升
// var 变量提升
console.log(a); // undefined(不是 ReferenceError)
var a = 5;
// 函数声明提升
sayHello(); // 'Hello'(可提前调用)
function sayHello() {
console.log('Hello');
}
// let/const 有暂时性死区(TDZ)
// console.log(b); // ReferenceError
let b = 10;
13.3 闭包
// 闭包:函数 + 其可访问的外部变量
function createCounter() {
let count = 0; // 私有变量
return {
increment() {
count++;
return count;
},
decrement() {
count--;
return count;
},
getValue() {
return count;
}
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.getValue()); // 2
// count 无法从外部直接访问
// 实用场景:防抖函数
function debounce(fn, delay) {
let timer = null;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
// 节流函数
function throttle(fn, interval) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= interval) {
lastTime = now;
fn.apply(this, args);
}
};
}
13.4 this 关键字
// this 的指向取决于调用方式
// 1. 全局上下文
console.log(this); // window(浏览器)/ global(Node.js)
// 2. 对象方法
const obj = {
name: '对象',
sayName() {
console.log(this.name); // this 指向 obj
}
};
obj.sayName();
// 3. 构造函数
function Person(name) {
this.name = name; // this 指向新创建的实例
}
const p = new Person('张三');
// 4. 事件处理
button.addEventListener('click', function() {
console.log(this); // this 指向 button 元素
});
// 5. 箭头函数(继承外层 this)
const obj2 = {
name: 'obj2',
methods: {
name: 'methods',
arrow: () => console.log(this.name), // 指向全局
normal() { console.log(this.name); } // 指向 methods
}
};
14. 原型与继承
14.1 原型链
// 每个对象都有 __proto__(隐式原型)
// 每个函数都有 prototype(显式原型)
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(`${this.name} 发出声音`);
};
const dog = new Animal('小狗');
dog.speak(); // '小狗 发出声音'
// 原型链
console.log(dog.__proto__ === Animal.prototype); // true
console.log(Animal.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null(原型链终点)
14.2 原型继承模式
// 寄生组合式继承(推荐)
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
function Child(name, age) {
Parent.call(this, name); // 继承实例属性
this.age = age;
}
// 继承原型方法
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child; // 修正 constructor 指向
// 添加子类方法
Child.prototype.sayAge = function() {
console.log(this.age);
};
const child = new Child('小明', 10);
child.sayName(); // '小明'
child.sayAge(); // 10
14.3 属性查找
// 检查属性是否在实例自身
console.log(child.hasOwnProperty('name')); // true
console.log(child.hasOwnProperty('sayName')); // false(在原型上)
// in 运算符(检查原型链)
console.log('sayName' in child); // true
// 获取对象原型
console.log(Object.getPrototypeOf(child) === Child.prototype); // true
// 设置对象原型
Object.setPrototypeOf(child, newProto);
15. 类(ES6+)
15.1 类声明
class Person {
// 构造函数
constructor(name, age) {
this.name = name;
this.age = age;
}
// 实例方法
introduce() {
return `我叫${this.name},${this.age}岁`;
}
// getter
get info() {
return `${this.name} (${this.age})`;
}
// setter
set rename(newName) {
if (typeof newName === 'string' && newName.length > 0) {
this.name = newName;
}
}
// 静态方法
static create(data) {
return new Person(data.name, data.age);
}
}
const person = new Person('张三', 30);
console.log(person.introduce());
console.log(person.info);
person.rename = '李四';
console.log(Person.create({ name: '王五', age: 25 }));
15.2 继承
class Employee extends Person {
constructor(name, age, position) {
super(name, age); // 必须先调用 super
this.position = position;
}
introduce() {
// 调用父类方法
return `${super.introduce()},职位是${this.position}`;
}
work() {
return `${this.name} 正在工作`;
}
}
const emp = new Employee('赵六', 28, '工程师');
console.log(emp.introduce()); // '我叫赵六,28岁,职位是工程师'
console.log(emp.work()); // '赵六 正在工作'
15.3 私有字段(ES2022)
class BankAccount {
#balance = 0; // 私有字段
constructor(initialBalance) {
this.#balance = initialBalance;
}
deposit(amount) {
if (amount > 0) {
this.#balance += amount;
}
}
getBalance() {
return this.#balance;
}
// 私有方法
#validateAmount(amount) {
return amount > 0 && Number.isFinite(amount);
}
}
const account = new BankAccount(1000);
account.deposit(500);
console.log(account.getBalance()); // 1500
// console.log(account.#balance); // ❌ SyntaxError
16. 异步编程
16.1 事件循环(Event Loop)
console.log('1'); // 同步
setTimeout(() => {
console.log('2'); // 宏任务
}, 0);
Promise.resolve().then(() => {
console.log('3'); // 微任务
});
console.log('4'); // 同步
// 输出顺序:1 → 4 → 3 → 2
执行顺序:同步代码 → 微任务队列(Promise、MutationObserver)→ 宏任务队列(setTimeout、setInterval、I/O)
16.2 回调函数
// 传统异步模式(容易产生回调地狱)
function fetchData(url, callback) {
setTimeout(() => {
const data = { result: 'some data' };
callback(null, data);
}, 1000);
}
fetchData('/api/data', (error, data) => {
if (error) {
console.error('出错:', error);
return;
}
console.log('数据:', data);
});
16.3 Promise
// 创建 Promise
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const success = true;
if (success) {
resolve('操作成功');
} else {
reject(new Error('操作失败'));
}
}, 1000);
});
// 使用 Promise
promise
.then(result => {
console.log(result);
return '下一步数据';
})
.then(data => {
console.log(data);
})
.catch(error => {
console.error('捕获错误:', error);
})
.finally(() => {
console.log('无论成功失败都会执行');
});
// Promise 静态方法
Promise.resolve('立即成功');
Promise.reject('立即失败');
Promise.all([p1, p2, p3]); // 全部成功才成功
Promise.allSettled([p1, p2, p3]); // 全部完成(无论成功失败)
Promise.race([p1, p2, p3]); // 第一个完成的
Promise.any([p1, p2, p3]); // 第一个成功的(ES2021)
16.4 async/await(ES2017)
// async 函数返回 Promise
async function fetchUser(id) {
// await 等待 Promise 完成
const response = await fetch(`/api/users/${id}`);
const user = await response.json();
return user;
}
// 错误处理
async function loadData() {
try {
const user = await fetchUser(1);
const posts = await fetchPosts(user.id);
return { user, posts };
} catch (error) {
console.error('加载失败:', error);
throw error; // 重新抛出或返回默认值
}
}
// 并行执行
async function loadMultiple() {
const [user, posts, settings] = await Promise.all([
fetchUser(1),
fetchPosts(1),
fetchSettings()
]);
return { user, posts, settings };
}
// 顶层 await(ES2022,模块中可用)
// const data = await fetch('/api/data');
16.5 Fetch API
// GET 请求
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error(error));
// POST 请求
async function postData(url, data) {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
return response.json();
}
// 请求超时
function fetchWithTimeout(url, timeout = 5000) {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('请求超时')), timeout)
)
]);
}
17. 错误处理
17.1 try-catch-finally
try {
// 可能出错的代码
const result = riskyOperation();
console.log(result);
} catch (error) {
// 处理错误
console.error('错误名称:', error.name);
console.error('错误信息:', error.message);
console.error('错误堆栈:', error.stack);
} finally {
// 始终执行(清理资源)
console.log('清理工作...');
}
17.2 自定义错误
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = 'ValidationError';
this.field = field;
}
}
function validateUser(user) {
if (!user.name) {
throw new ValidationError('用户名不能为空', 'name');
}
if (user.age < 0) {
throw new ValidationError('年龄不能为负数', 'age');
}
}
try {
validateUser({ name: '', age: -5 });
} catch (error) {
if (error instanceof ValidationError) {
console.log(`${error.field} 字段验证失败: ${error.message}`);
}
}
17.3 全局错误处理
// 浏览器全局错误
window.onerror = function(message, source, line, col, error) {
console.error('全局错误:', message, source, line, col);
// 上报错误到监控系统
};
// 未捕获的 Promise 拒绝
window.addEventListener('unhandledrejection', function(event) {
console.error('未处理的 Promise 拒绝:', event.reason);
event.preventDefault(); // 阻止默认的控制台输出
});
18. 模块化
18.1 ES Modules(ESM)
// ============ math.js ============
// 命名导出
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
// 默认导出(一个模块只能有一个)
export default function subtract(a, b) {
return a - b;
}
// ============ app.js ============
// 命名导入
import { PI, add, multiply } from './math.js';
// 默认导入
import subtract from './math.js';
// 别名导入
import { add as sum } from './math.js';
// 全部导入
import * as math from './math.js';
console.log(math.PI, math.add(1, 2));
// 动态导入(返回 Promise)
async function loadModule() {
const module = await import('./dynamic.js');
module.default();
}
// 仅执行模块(不导入任何内容)
import './init.js';
18.2 CommonJS(Node.js 传统)
// ============ utils.js ============
function formatDate(date) {
return date.toISOString();
}
function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
// 导出
module.exports = {
formatDate,
capitalize
};
// 或逐个导出
exports.formatDate = formatDate;
// ============ main.js ============
const utils = require('./utils.js');
// 或解构
const { formatDate } = require('./utils.js');
19. ES6+ 常用新特性
19.1 展开与剩余
// 展开运算符(Spread)...
const arr = [1, 2, 3];
console.log(Math.max(...arr)); // 展开为参数
const copy = [...arr]; // 浅拷贝数组
const merged = { ...obj1, ...obj2 }; // 合并对象
// 剩余参数(Rest)
function sum(first, ...rest) {
return first + rest.reduce((a, b) => a + b, 0);
}
const [head, ...tail] = [1, 2, 3, 4]; // 数组解构剩余
19.2 Set 和 Map
// Set — 不重复值的集合
const set = new Set([1, 2, 2, 3, 3]);
console.log(set); // Set { 1, 2, 3 }
set.add(4);
set.has(2); // true
set.delete(3);
set.size; // 3
set.clear();
// 数组去重
const unique = [...new Set([1, 2, 2, 3, 3])]; // [1, 2, 3]
// Map — 键值对集合(键可以是任意类型)
const map = new Map();
map.set('name', '张三');
map.set(42, '数字键');
map.set({ id: 1 }, '对象键');
map.get('name'); // '张三'
map.has(42); // true
map.size; // 3
map.delete('name');
// 遍历
for (const [key, value] of map) {
console.log(key, value);
}
// WeakMap — 键必须是对象,弱引用(可被垃圾回收)
const weakMap = new WeakMap();
const objKey = {};
weakMap.set(objKey, '关联数据');
// 当 objKey 被回收时,该条目自动从 WeakMap 中移除
19.3 可选链与空值合并
// 可选链(Optional Chaining)— ES2020
const user = null;
console.log(user?.profile?.name); // undefined(不报错)
console.log(user?.getInfo?.()); // undefined(方法调用)
console.log(arr?.[0]); // undefined(数组访问)
// 空值合并(Nullish Coalescing)— ES2020
const value = null ?? '默认值'; // '默认值'
const value2 = 0 ?? '默认值'; // 0(只有 null/undefined 触发)
const value3 = '' || '默认值'; // '默认值'(|| 将所有假值触发)
// 结合使用
const displayName = user?.name ?? '匿名用户';
19.4 逻辑赋值运算符(ES2021)
let a = null;
a ??= '默认值'; // a = a ?? '默认值'
let b = 0;
b ||= 10; // b = b || 10 → 10
let c = 5;
c &&= 10; // c = c && 10 → 10
20. Web API 精选
20.1 localStorage / sessionStorage
// localStorage — 持久化存储(浏览器关闭后保留)
localStorage.setItem('theme', 'dark');
const theme = localStorage.getItem('theme');
localStorage.removeItem('theme');
localStorage.clear();
// 存储对象(需序列化)
const user = { name: '张三', age: 30 };
localStorage.setItem('user', JSON.stringify(user));
const savedUser = JSON.parse(localStorage.getItem('user'));
// sessionStorage — 会话级存储(关闭标签页后清除)
sessionStorage.setItem('temp', '临时数据');
20.2 定时器
// setTimeout — 延迟执行
const timerId = setTimeout(() => {
console.log('1秒后执行');
}, 1000);
clearTimeout(timerId); // 取消
// setInterval — 重复执行
const intervalId = setInterval(() => {
console.log('每2秒执行一次');
}, 2000);
clearInterval(intervalId); // 停止
// requestAnimationFrame — 动画优化
function animate() {
// 执行动画逻辑
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
20.3 History API
// 导航
history.back();
history.forward();
history.go(-1); // 后退一页
history.go(2); // 前进两页
// 操作历史记录(SPA 路由基础)
history.pushState({ page: 1 }, '标题', '/page1');
history.replaceState({ page: 2 }, '标题', '/page2');
// 监听导航
window.addEventListener('popstate', (event) => {
console.log('当前状态:', event.state);
console.log('当前 URL:', location.pathname);
});
20.4 Intersection Observer
// 观察元素是否进入视口
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log('元素进入视口');
entry.target.classList.add('visible');
observer.unobserve(entry.target); // 停止观察
}
});
}, {
threshold: 0.5, // 50% 可见时触发
rootMargin: '20px' // 提前 20px 触发
});
// 开始观察
const elements = document.querySelectorAll('.lazy-load');
elements.forEach(el => observer.observe(el));
20.5 剪贴板 API
// 写入剪贴板
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
console.log('已复制到剪贴板');
} catch (err) {
console.error('复制失败:', err);
}
}
// 读取剪贴板
async function readFromClipboard() {
try {
const text = await navigator.clipboard.readText();
console.log('剪贴板内容:', text);
} catch (err) {
console.error('读取失败:', err);
}
}
21. 设计模式
21.1 单例模式
class Database {
static #instance = null;
constructor() {
if (Database.#instance) {
return Database.#instance;
}
this.connection = this.#connect();
Database.#instance = this;
}
#connect() {
console.log('建立数据库连接');
return { /* 连接对象 */ };
}
static getInstance() {
if (!Database.#instance) {
Database.#instance = new Database();
}
return Database.#instance;
}
}
const db1 = Database.getInstance();
const db2 = Database.getInstance();
console.log(db1 === db2); // true
21.2 观察者模式
class EventEmitter {
constructor() {
this.events = {};
}
on(event, listener) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(listener);
return () => this.off(event, listener); // 返回取消订阅函数
}
off(event, listener) {
if (!this.events[event]) return;
this.events[event] = this.events[event].filter(l => l !== listener);
}
emit(event, ...args) {
if (!this.events[event]) return;
this.events[event].forEach(listener => listener(...args));
}
once(event, listener) {
const wrapper = (...args) => {
listener(...args);
this.off(event, wrapper);
};
this.on(event, wrapper);
}
}
// 使用
const emitter = new EventEmitter();
const unsubscribe = emitter.on('data', (data) => console.log('收到:', data));
emitter.emit('data', { message: 'Hello' });
unsubscribe(); // 取消订阅
21.3 工厂模式
class User {
constructor(name, role) {
this.name = name;
this.role = role;
}
}
class Admin extends User {
constructor(name) {
super(name, 'admin');
this.permissions = ['read', 'write', 'delete'];
}
}
class Guest extends User {
constructor(name) {
super(name, 'guest');
this.permissions = ['read'];
}
}
class UserFactory {
static createUser(name, type) {
switch (type.toLowerCase()) {
case 'admin':
return new Admin(name);
case 'guest':
return new Guest(name);
default:
throw new Error(`未知用户类型: ${type}`);
}
}
}
const admin = UserFactory.createUser('张三', 'admin');
const guest = UserFactory.createUser('访客', 'guest');
22. 性能优化
22.1 DOM 操作优化
// ❌ 避免:频繁操作 DOM
for (let i = 0; i < 1000; i++) {
document.body.innerHTML += `<div>${i}</div>`;
}
// ✅ 推荐:使用 DocumentFragment 或一次性插入
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const div = document.createElement('div');
div.textContent = i;
fragment.appendChild(div);
}
document.body.appendChild(fragment);
// ✅ 或使用 innerHTML 一次性设置
let html = '';
for (let i = 0; i < 1000; i++) {
html += `<div>${i}</div>`;
}
document.body.innerHTML = html;
22.2 防抖与节流
// 防抖 — 连续触发只执行最后一次
function debounce(fn, delay = 300) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
// 节流 — 固定时间间隔执行一次
function throttle(fn, interval = 300) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= interval) {
lastTime = now;
fn.apply(this, args);
}
};
}
// 使用示例
const handleSearch = debounce((query) => {
console.log('搜索:', query);
}, 500);
const handleScroll = throttle(() => {
console.log('滚动位置:', window.scrollY);
}, 200);
22.3 内存管理
// 避免内存泄漏
// ❌ 全局变量
function leak() {
globalVar = '这会泄漏到全局'; // 忘记 var/let/const
}
// ❌ 未清除的定时器
let intervalId = setInterval(() => {
// 如果组件销毁了但定时器还在运行
}, 1000);
// ✅ 在不需要时清除
clearInterval(intervalId);
// ❌ 闭包中的大对象引用
function createHandler() {
const largeData = new Array(1000000);
return function() {
console.log(largeData[0]); // 保留了对 largeData 的引用
};
}
// ❌ 未移除的事件监听
// ✅ 组件销毁时移除
element.removeEventListener('click', handler);
// ✅ 使用 WeakMap 存储 DOM 关联数据
const elementData = new WeakMap();
elementData.set(document.getElementById('app'), { clicks: 0 });
// 当元素被移除时,关联数据自动被回收
22.4 代码分割与懒加载
// 动态导入(代码分割)
button.addEventListener('click', async () => {
const { heavyFunction } = await import('./heavyModule.js');
heavyFunction();
});
// 图片懒加载
const imageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
imageObserver.unobserve(img);
}
});
});
document.querySelectorAll('img[data-src]').forEach(img => {
imageObserver.observe(img);
});
23. 最佳实践
23.1 严格模式
'use strict'; // 写在文件或函数开头
// 严格模式下的变化:
// 1. 变量必须先声明
x = 10; // ❌ ReferenceError
// 2. 函数 this 为 undefined(非对象包装)
function showThis() {
console.log(this); // undefined(非严格模式为 window)
}
// 3. 禁止删除变量
let y = 5;
// delete y; // ❌ SyntaxError
// 4. 函数参数不能重名
// function(a, a) {} // ❌
23.2 代码风格
// ✅ 使用 const 优先,其次是 let,避免 var
const MAX_SIZE = 100;
let currentSize = 0;
// ✅ 使用 === 而非 ==
if (value === null || value === undefined) { }
// 或
if (value == null) { } // 仅检查 null/undefined 时 == null 可接受
// ✅ 使用模板字符串
const message = `用户 ${name} 的年龄是 ${age}`;
// ✅ 使用解构
const { name, age } = user;
const [first, ...rest] = items;
// ✅ 使用展开运算符进行浅拷贝
const copy = { ...original };
const arrCopy = [...originalArray];
// ✅ 提前 return 减少嵌套
function process(data) {
if (!data) return null;
if (!data.isValid) return null;
// 主逻辑
return transform(data);
}
// ✅ 使用可选链
const city = user?.address?.city;
23.3 安全实践
// 防止 XSS
const userInput = '<img src=x onerror=alert("XSS")>';
// ❌ 直接插入 HTML
element.innerHTML = userInput;
// ✅ 使用 textContent 或进行转义
element.textContent = userInput;
// HTML 转义函数
function escapeHtml(str) {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
// 安全的 DOM 操作
const sanitized = DOMPurify.sanitize(userInput); // 使用 DOMPurify 库
element.innerHTML = sanitized;
// 避免 eval
// ❌ eval('console.log("危险")');
// ❌ new Function('return ' + userInput)();
24. 测试入门
24.1 单元测试概念
// 被测试的函数
function add(a, b) {
return a + b;
}
function divide(a, b) {
if (b === 0) throw new Error('除数不能为零');
return a / b;
}
// 简单的手动测试
function test(description, fn) {
try {
fn();
console.log(`✅ ${description}`);
} catch (error) {
console.error(`❌ ${description}`);
console.error(error);
}
}
function expect(actual) {
return {
toBe(expected) {
if (actual !== expected) {
throw new Error(`期望 ${expected},实际 ${actual}`);
}
},
toThrow() {
let threw = false;
try { actual(); } catch { threw = true; }
if (!threw) throw new Error('期望抛出错误');
}
};
}
test('add(1, 2) 应返回 3', () => {
expect(add(1, 2)).toBe(3);
});
test('divide(10, 0) 应抛出错误', () => {
expect(() => divide(10, 0)).toThrow();
});
24.2 Jest 示例
// math.js
export function multiply(a, b) {
return a * b;
}
// math.test.js
import { multiply } from './math';
describe('multiply 函数', () => {
test('正数相乘', () => {
expect(multiply(3, 4)).toBe(12);
});
test('乘以零', () => {
expect(multiply(5, 0)).toBe(0);
});
test('负数相乘', () => {
expect(multiply(-2, 3)).toBe(-6);
});
});
25. 现代工具链
25.1 包管理器
# npm
npm init -y # 初始化项目
npm install package-name # 安装依赖
npm install -D package-name # 开发依赖
npm update # 更新依赖
npm run build # 运行脚本
# yarn
yarn add package-name
yarn add -D package-name
# pnpm(更快、更省空间)
pnpm add package-name
25.2 打包工具概念
// webpack.config.js 示例结构
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({ template: './index.html' })
]
};
// Vite 配置(更简洁)
// vite.config.js
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [],
server: { port: 3000 }
});
25.3 Babel 转译
// babel.config.json
{
"presets": [
["@babel/preset-env", {
"targets": "> 0.25%, not dead"
}]
],
"plugins": [
"@babel/plugin-transform-runtime"
]
}
附录
A. 常用代码片段
// 生成随机数
const random = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
// 打乱数组(Fisher-Yates)
const shuffle = (arr) => {
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
};
// 深拷贝(简易版)
const deepClone = (obj) => structuredClone(obj); // 现代浏览器支持
// 或
const deepCloneJSON = (obj) => JSON.parse(JSON.stringify(obj));
// 格式化日期
const formatDate = (date) => {
return new Date(date).toLocaleDateString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
};
// 检测设备类型
const isMobile = /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
B. 推荐学习资源
-
MDN Web Docs — 最权威的 JS 参考文档
-
JavaScript.info — 现代 JS 教程
-
You Don't Know JS — 深入理解 JS 系列
-
ECMAScript 规范 — 语言标准
C. 持续学习路径
掌握基础 → 2. DOM & 事件 → 3. 异步编程 → 4. ES6+ 特性 → 5. 框架学习(React/Vue)→ 6. Node.js 后端 → 7. TypeScript → 8. 工程化(Webpack/Vite)→ 9. 性能优化 → 10. 源码阅读
(注:文档部分内容可能由 AI 生成)