十、模块化
10.1 ES6 模块
// math.js —— 导出
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export default class Calculator {
static multiply(a, b) {
return a * b;
}
}
// 统一导出
const subtract = (a, b) => a - b;
const divide = (a, b) => a / b;
export { subtract, divide };
// 重命名导出
export { subtract as sub };
// app.js —— 导入
import Calculator, { PI, add, subtract as sub } from "./math.js";
import * as MathUtils from "./math.js"; // 导入所有
console.log(PI); // 3.14159
console.log(add(5, 3)); // 8
console.log(sub(10, 3)); // 7
console.log(Calculator.multiply(4, 5)); // 20
console.log(MathUtils.divide(10, 2)); // 5
// 动态导入(代码分割)
const module = await import("./math.js");
console.log(module.add(2, 3));
10.2 CommonJS(Node.js)
// math.js —— 导出
const PI = 3.14159;
function add(a, b) {
return a + b;
}
class Calculator {
static multiply(a, b) {
return a * b;
}
}
module.exports = {
PI,
add,
Calculator
};
// 或
exports.PI = PI;
exports.add = add;
// app.js —— 导入
const math = require("./math.js");
const { PI, add, Calculator } = require("./math.js");
console.log(PI);
console.log(add(5, 3));
console.log(Calculator.multiply(4, 5));
10.3 模块化最佳实践
// 单一职责原则
// user.service.js —— 只负责用户相关逻辑
export class UserService {
async getUser(id) {
// 获取用户逻辑
}
async updateUser(id, data) {
// 更新用户逻辑
}
}
// 避免循环依赖
// ❌ 错误:moduleA 依赖 moduleB,moduleB 依赖 moduleA
// 使用索引文件简化导入
// components/index.js
export { Button } from "./Button";
export { Input } from "./Input";
export { Modal } from "./Modal";
// 使用时
import { Button, Input, Modal } from "./components";
十一、现代 JavaScript 特性(ES6+)
11.1 解构赋值
// 数组解构
const arr = [1, 2, 3, 4];
const [a, b, ...rest] = arr;
console.log(a, b, rest); // 1, 2, [3,4]
// 交换变量
let x = 1, y = 2;
[x, y] = [y, x];
// 对象解构
const person = { name: "张三", age: 25, city: "北京" };
const { name, age, country = "中国" } = person;
console.log(name, age, country); // 张三 25 中国
// 重命名
const { name: userName } = person;
// 嵌套解构
const user = {
id: 1,
profile: {
nickname: "小明",
address: { city: "上海" }
}
};
const { profile: { nickname, address: { city } } } = user;
// 函数参数解构
function greet({ name, age = 18 }) {
console.log(`你好,${name},${age}岁`);
}
11.2 展开运算符
// 数组展开
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2]; // [1,2,3,4,5,6]
// 复制数组
const copy = [...arr1];
// 对象展开
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const merged = { ...obj1, ...obj2 }; // {a:1,b:2,c:3,d:4}
// 函数调用
const numbers = [1, 2, 3, 4];
const max = Math.max(...numbers);
// 剩余参数
function log(...args) {
console.log(args); // 数组形式
}
11.3 模板字符串
// 基础插值
const name = "张三";
const greeting = `你好,${name}`;
// 表达式
const price = 100;
const tax = 0.1;
const total = `总价: ${price * (1 + tax)}元`;
// 多行字符串
const html = `
<div>
<h1>标题</h1>
<p>内容</p>
</div>
`;
// 标签模板函数
function highlight(strings, ...values) {
return strings.reduce((result, str, i) => {
return result + str + (values[i] ? `<strong>${values[i]}</strong>` : "");
}, "");
}
const user = "张三";
const age = 25;
const message = highlight`用户 ${user} 今年 ${age} 岁`;
// 输出: "用户 <strong>张三</strong> 今年 <strong>25</strong> 岁"
11.4 可选链与空值合并
// 可选链(?.)
const user = {
name: "张三",
profile: {
address: {
city: "北京"
}
}
};
// 安全访问深层属性
const city = user?.profile?.address?.city; // "北京"
const country = user?.profile?.address?.country; // undefined
const zip = user?.profile?.address?.zip ?? "100000";
// 可选链调用方法
const result = user.getAddress?.();
// 空值合并(??)
const value = null;
const defaultValue = value ?? "默认值"; // "默认值"
// 与 || 的区别:?? 只对 null/undefined 生效
const count = 0;
console.log(count || 10); // 10
console.log(count ?? 10); // 0
11.5 其他重要特性
// BigInt(大整数)
const bigNumber = 9007199254740991n;
const result = bigNumber + 1n;
// 指数运算符
console.log(2 ** 10); // 1024
// Array.prototype.includes
console.log([1, 2, 3].includes(2)); // true
// 对象属性简写
const name = "张三";
const age = 25;
const user = { name, age }; // 等同于 { name: name, age: age }
// 计算属性名
const key = "dynamic";
const obj = {
[key]: "动态键",
[`${key}2`]: "模板键"
};
// 私有字段(ES2022)
class MyClass {
#privateField = 42;
#privateMethod() {
return this.#privateField;
}
publicMethod() {
return this.#privateMethod();
}
}
// 顶级 await(ES2022)
const data = await fetch("/api/data");
十二、性能优化技巧
12.1 内存管理
// 避免全局变量
// 错误
globalVar = "污染全局";
// 正确
let localVar = "局部变量";
// 及时清理引用
let largeObject = { data: new Array(1000000) };
largeObject = null; // 帮助垃圾回收
// 使用 WeakMap 避免内存泄漏
const cache = new WeakMap();
let element = document.getElementById("app");
cache.set(element, { data: "大量数据" });
element = null; // element 被移除后,缓存自动清理
// 避免闭包导致的内存泄漏
function createHandler() {
let bigData = new Array(10000);
return function() {
// 如果一直持有 bigData 引用,无法被回收
console.log(bigData.length);
};
}
12.2 代码优化
// 循环优化
// 缓存数组长度
const arr = [1, 2, 3, 4, 5];
for (let i = 0, len = arr.length; i < len; i++) {
// 循环体
}
// 使用 for...of 替代 forEach(支持 break)
for (const item of arr) {
if (item === 3) break;
}
// 避免频繁的 DOM 操作(见 7.6)
// 事件委托(见 7.5)
// 使用 requestAnimationFrame 实现动画
function smoothScroll(targetY, duration) {
const startY = window.scrollY;
const distance = targetY - startY;
const startTime = performance.now();
function scroll(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
window.scrollTo(0, startY + distance * progress);
if (progress < 1) {
requestAnimationFrame(scroll);
}
}
requestAnimationFrame(scroll);
}
// 图片懒加载
const images = document.querySelectorAll("img[data-src]");
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
});
images.forEach(img => observer.observe(img));
12.3 网络优化
// 防抖和节流(见 7.6)
// 资源预加载
<link rel="preload" href="critical.css" as="style">
<link rel="preconnect" href="https://api.example.com">
// 使用 Web Worker 处理耗时任务
// worker.js
self.addEventListener("message", (e) => {
const result = heavyComputation(e.data);
self.postMessage(result);
});
// 主线程
const worker = new Worker("worker.js");
worker.postMessage(data);
worker.onmessage = (e) => {
console.log("计算结果:", e.data);
};
// 使用缓存策略(Service Worker)
// 实现离线应用