阅读视图

发现新文章,点击刷新页面。

「周更第10期」实用JS库推荐:VueUse

引言

在Vue 3的生态系统中,组合式API(Composition API)为我们带来了更灵活的代码组织方式。而VueUse作为Vue生态中最受欢迎的工具库之一,为开发者提供了200多个实用的组合式函数,极大地简化了日常开发工作。本期我们将深入探索VueUse这个强大的工具库。

库介绍

基本信息

  • 库名称:VueUse
  • GitHub Stars:19.5k+ ⭐
  • 维护状态:活跃维护,定期更新
  • 兼容性:Vue 2.7+ / Vue 3.0+
  • 包大小:Tree-shakable,按需引入
  • 依赖关系:基于Vue响应式系统

核心特点

VueUse是一个基于Vue组合式API的实用工具库,它将常见的开发需求封装成可复用的组合式函数。主要特点包括:

  1. 丰富的功能集合:提供200+个实用函数,涵盖状态管理、浏览器API、动画、网络请求等各个方面
  2. 类型安全:完整的TypeScript支持,提供优秀的开发体验
  3. Tree-shakable:支持按需引入,不会增加不必要的包体积
  4. SSR友好:完美支持服务端渲染
  5. 灵活可配置:大部分函数都提供丰富的配置选项
  6. 响应式设计:充分利用Vue的响应式系统

安装使用

安装命令

# 使用 pnpm(推荐)
pnpm add @vueuse/core

# 安装额外的集成包
pnpm add @vueuse/components @vueuse/integrations

基础使用示例

<template>
  <div>
    <p>鼠标位置: {{ x }}, {{ y }}</p>
    <p>窗口大小: {{ width }} x {{ height }}</p>
    <button @click="toggle">
      {{ isOnline ? '在线' : '离线' }}
    </button>
  </div>
</template>

<script setup>
import { useMouse, useWindowSize, useOnline } from '@vueuse/core'

// 获取鼠标位置
const { x, y } = useMouse()

// 获取窗口大小
const { width, height } = useWindowSize()

// 检测网络状态
const { isOnline, toggle } = useOnline()
</script>

VueUse API分类详解

1. 状态管理类

useLocalStorage / useSessionStorage

// 响应式的本地存储
const name = useLocalStorage('user-name', '默认值')
const settings = useSessionStorage('app-settings', { theme: 'light' })

useToggle

// 布尔值切换
const [isVisible, toggle] = useToggle()
const [status, toggleStatus] = useToggle('active', 'inactive')

useCounter

// 计数器管理
const { count, inc, dec, set, reset } = useCounter(0, { min: 0, max: 100 })

2. 浏览器API类

useMouse / useMousePressed

// 鼠标位置和状态
const { x, y } = useMouse()
const { pressed } = useMousePressed()

useKeyboard / useKeyModifier

// 键盘事件
const { ctrl, shift, alt, meta } = useKeyModifier()
const keys = useKeyboard()

useClipboard

// 剪贴板操作
const { text, copy, copied, isSupported } = useClipboard()

useFullscreen

// 全屏控制
const { isFullscreen, enter, exit, toggle } = useFullscreen()

3. 传感器类

useDeviceOrientation

// 设备方向
const { alpha, beta, gamma, absolute } = useDeviceOrientation()

useGeolocation

// 地理位置
const { coords, locatedAt, error } = useGeolocation()

useBattery

// 电池状态
const { charging, level, dischargingTime } = useBattery()

4. 网络类

useFetch

// HTTP请求
const { data, error, isFetching } = useFetch('/api/users')

useWebSocket

// WebSocket连接
const { status, data, send, open, close } = useWebSocket('ws://localhost:8080')

5. 动画类

useTransition

// 数值过渡动画
const source = ref(0)
const output = useTransition(source, {
  duration: 1000,
  transition: TransitionPresets.easeInOutCubic
})

useInterval / useTimeout

// 定时器管理
const { pause, resume, isActive } = useInterval(() => {
  console.log('每秒执行')
}, 1000)

6. 元素操作类

useElementSize

// 元素尺寸监听
const el = ref()
const { width, height } = useElementSize(el)

useIntersectionObserver

// 元素可见性检测
const target = ref()
const { isIntersecting } = useIntersectionObserver(target)

useResizeObserver

// 元素大小变化监听
const el = ref()
useResizeObserver(el, (entries) => {
  console.log('元素大小改变')
})

7. 实用工具类

useDebounce / useThrottle

// 防抖和节流
const input = ref('')
const debouncedValue = useDebounce(input, 500)
const throttledValue = useThrottle(input, 1000)

useAsyncState

// 异步状态管理
const { state, isReady, isLoading, error, execute } = useAsyncState(
  () => fetchUserData(),
  null
)

useVModel

// 双向绑定辅助
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
const data = useVModel(props, 'modelValue', emit)

实际应用场景

1. 响应式布局系统

<template>
  <div class="responsive-layout">
    <aside v-if="!isMobile" class="sidebar">
      侧边栏内容
    </aside>
    <main :class="{ 'full-width': isMobile }">
      <header>
        <button @click="toggleTheme">
          {{ isDark ? '🌙' : '☀️' }}
        </button>
      </header>
      <div class="content">
        主要内容区域
      </div>
    </main>
  </div>
</template>

<script setup>
import { useBreakpoints, useDark, useToggle } from '@vueuse/core'

// 响应式断点
const breakpoints = useBreakpoints({
  mobile: 768,
  tablet: 1024,
  desktop: 1280
})

const isMobile = breakpoints.smaller('tablet')

// 暗黑模式
const isDark = useDark()
const toggleTheme = useToggle(isDark)
</script>

2. 实时数据监控面板

<template>
  <div class="dashboard">
    <div class="status-bar">
      <span :class="{ online: isOnline, offline: !isOnline }">
        {{ isOnline ? '在线' : '离线' }}
      </span>
      <span>电池: {{ batteryLevel }}%</span>
      <span>{{ formattedTime }}</span>
    </div>
    
    <div class="charts">
      <canvas ref="chartCanvas"></canvas>
    </div>
    
    <div class="controls">
      <button @click="startMonitoring" :disabled="isMonitoring">
        开始监控
      </button>
      <button @click="stopMonitoring" :disabled="!isMonitoring">
        停止监控
      </button>
    </div>
  </div>
</template>

<script setup>
import { 
  useOnline, 
  useBattery, 
  useNow, 
  useInterval,
  useElementSize 
} from '@vueuse/core'

// 网络状态
const isOnline = useOnline()

// 电池状态
const { level: batteryLevel } = useBattery()

// 实时时间
const now = useNow()
const formattedTime = computed(() => 
  now.value.toLocaleTimeString()
)

// 图表容器
const chartCanvas = ref()
const { width, height } = useElementSize(chartCanvas)

// 监控控制
const isMonitoring = ref(false)
const { pause, resume } = useInterval(() => {
  // 更新图表数据
  updateChartData()
}, 1000, { immediate: false })

const startMonitoring = () => {
  isMonitoring.value = true
  resume()
}

const stopMonitoring = () => {
  isMonitoring.value = false
  pause()
}
</script>

3. 高级表单处理

<template>
  <form @submit.prevent="handleSubmit">
    <div class="form-group">
      <input 
        v-model="form.email" 
        type="email" 
        placeholder="邮箱"
        :class="{ error: emailError }"
      >
      <span v-if="emailError" class="error-text">
        {{ emailError }}
      </span>
    </div>
    
    <div class="form-group">
      <input 
        v-model="form.password" 
        type="password" 
        placeholder="密码"
      >
    </div>
    
    <button 
      type="submit" 
      :disabled="!isFormValid || isSubmitting"
    >
      {{ isSubmitting ? '提交中...' : '登录' }}
    </button>
    
    <div v-if="submitError" class="error">
      {{ submitError }}
    </div>
  </form>
</template>

<script setup>
import { 
  useVModel, 
  useAsyncValidator, 
  useAsyncState,
  useDebounce 
} from '@vueuse/core'

// 表单数据
const form = reactive({
  email: '',
  password: ''
})

// 邮箱验证(防抖)
const debouncedEmail = useDebounce(() => form.email, 500)
const { pass: isEmailValid, errorMessage: emailError } = useAsyncValidator(
  debouncedEmail,
  async (value) => {
    if (!value) return '邮箱不能为空'
    if (!/\S+@\S+\.\S+/.test(value)) return '邮箱格式不正确'
    
    // 异步验证邮箱是否已注册
    const exists = await checkEmailExists(value)
    if (!exists) return '邮箱未注册'
    
    return true
  }
)

// 表单验证
const isFormValid = computed(() => 
  isEmailValid.value && form.password.length >= 6
)

// 提交处理
const { 
  state: submitResult, 
  isLoading: isSubmitting, 
  error: submitError, 
  execute: submitForm 
} = useAsyncState(
  () => loginUser(form),
  null,
  { immediate: false }
)

const handleSubmit = () => {
  if (isFormValid.value) {
    submitForm()
  }
}
</script>

优缺点分析

优势

  1. 开发效率高:提供大量开箱即用的功能,减少重复代码编写
  2. 类型安全:完整的TypeScript支持,减少运行时错误
  3. 性能优秀:基于Vue响应式系统,性能表现良好
  4. 文档完善:官方文档详细,示例丰富
  5. 社区活跃:持续更新,bug修复及时
  6. 灵活性强:大部分函数都支持自定义配置

局限性

  1. 学习成本:函数众多,需要时间熟悉各个API
  2. 包体积:虽然支持tree-shaking,但完整引入会增加包体积
  3. Vue绑定:仅适用于Vue项目,不能在其他框架中使用
  4. 版本依赖:需要Vue 2.7+或Vue 3.0+

最佳实践建议

1. 按需引入

// 推荐:按需引入
import { useMouse, useLocalStorage } from '@vueuse/core'

// 避免:全量引入
import * as VueUse from '@vueuse/core'

2. 合理使用响应式

// 合理利用响应式特性
const { x, y } = useMouse()
const position = computed(() => `${x.value}, ${y.value}`)

// 避免不必要的响应式转换
const staticConfig = { timeout: 5000 } // 静态配置无需响应式

3. 错误处理

const { data, error, isFinished } = useFetch('/api/data')

watchEffect(() => {
  if (error.value) {
    console.error('请求失败:', error.value)
    // 处理错误逻辑
  }
})

总结

VueUse是Vue生态系统中不可或缺的工具库,它通过提供丰富的组合式函数,极大地提升了Vue开发的效率和体验。无论是处理浏览器API、状态管理,还是实现复杂的交互效果,VueUse都能提供优雅的解决方案。

推荐使用场景:

  • Vue 3项目的快速开发
  • 需要大量浏览器API交互的应用
  • 追求代码复用性和可维护性的项目
  • 需要响应式状态管理的复杂应用

VueUse不仅是一个工具库,更是学习Vue组合式API最佳实践的优秀范例。通过研究其源码和使用方式,能够帮助开发者更好地理解和运用Vue 3的核心特性。

相关链接

🌐 HTML DOM API全攻略(下篇)- 高级接口与现代Web开发实践

📚 学习目标

  • 掌握HTML DOM API的后9个高级接口
  • 理解现代Web开发中的复杂应用场景
  • 学会构建高性能、可扩展的Web应用
  • 掌握前沿技术的实际应用方法

🎯 难度等级

高级 - 适合有扎实JavaScript基础和Web开发经验的开发者

🏷️ 技术标签

JavaScript DOM API Web开发 性能优化 现代前端

⏱️ 阅读时间

约18-25分钟


🚀 引言

在上篇文章中,我们深入探讨了HTML DOM API的前9个核心接口。本篇将继续这一技术之旅,重点介绍更加高级和专业的API接口,这些接口是构建现代Web应用不可或缺的技术基础。

从多线程处理到实时通信,从3D图形渲染到音频处理,这些高级API将帮助你构建更加强大和用户友好的Web应用。

🎯 高级API详解

10. Web Worker接口:多线程处理的利器

🔍 应用场景

大数据处理、复杂计算、图像处理、后台任务

❌ 常见问题

// ❌ 主线程执行耗时操作,阻塞UI
function processLargeData(data) {
    let result = [];
    for (let i = 0; i < data.length; i++) {
        // 复杂计算
        result.push(heavyComputation(data[i]));
    }
    return result;
}

// UI会被阻塞
const result = processLargeData(largeDataSet);
updateUI(result);

✅ 推荐方案

// ✅ Web Worker管理器
class WorkerManager {
    constructor() {
        this.workers = new Map();
        this.taskQueue = [];
        this.maxWorkers = navigator.hardwareConcurrency || 4;
    }
    
    /**
     * 创建Worker
     * @param {string} name - Worker名称
     * @param {string} scriptPath - Worker脚本路径
     * @returns {Promise<Worker>}
     */
    async createWorker(name, scriptPath) {
        try {
            const worker = new Worker(scriptPath);
            
            // 设置错误处理
            worker.onerror = (error) => {
                console.error(`Worker ${name} error:`, error);
                this.removeWorker(name);
            };
            
            // 设置消息处理
            worker.onmessage = (event) => {
                this.handleWorkerMessage(name, event);
            };
            
            this.workers.set(name, {
                worker,
                busy: false,
                tasks: new Map()
            });
            
            return worker;
        } catch (error) {
            console.error('Failed to create worker:', error);
            throw error;
        }
    }
    
    /**
     * 执行任务
     * @param {string} workerName - Worker名称
     * @param {string} taskType - 任务类型
     * @param {*} data - 任务数据
     * @returns {Promise}
     */
    async executeTask(workerName, taskType, data) {
        const workerInfo = this.workers.get(workerName);
        if (!workerInfo) {
            throw new Error(`Worker ${workerName} not found`);
        }
        
        const taskId = this.generateTaskId();
        
        return new Promise((resolve, reject) => {
            // 存储任务回调
            workerInfo.tasks.set(taskId, { resolve, reject });
            
            // 发送任务到Worker
            workerInfo.worker.postMessage({
                taskId,
                type: taskType,
                data
            });
            
            workerInfo.busy = true;
        });
    }
    
    /**
     * 处理Worker消息
     * @param {string} workerName - Worker名称
     * @param {MessageEvent} event - 消息事件
     */
    handleWorkerMessage(workerName, event) {
        const { taskId, result, error } = event.data;
        const workerInfo = this.workers.get(workerName);
        
        if (!workerInfo) return;
        
        const task = workerInfo.tasks.get(taskId);
        if (!task) return;
        
        // 清理任务
        workerInfo.tasks.delete(taskId);
        workerInfo.busy = false;
        
        // 执行回调
        if (error) {
            task.reject(new Error(error));
        } else {
            task.resolve(result);
        }
    }
    
    /**
     * 批量处理任务
     * @param {Array} tasks - 任务数组
     * @param {string} workerScript - Worker脚本
     * @returns {Promise<Array>}
     */
    async processBatch(tasks, workerScript) {
        const results = [];
        const workers = [];
        
        // 创建Worker池
        for (let i = 0; i < Math.min(this.maxWorkers, tasks.length); i++) {
            const workerName = `batch-worker-${i}`;
            await this.createWorker(workerName, workerScript);
            workers.push(workerName);
        }
        
        // 分配任务
        const promises = tasks.map((task, index) => {
            const workerName = workers[index % workers.length];
            return this.executeTask(workerName, task.type, task.data);
        });
        
        try {
            const results = await Promise.all(promises);
            return results;
        } finally {
            // 清理Worker
            workers.forEach(name => this.removeWorker(name));
        }
    }
    
    /**
     * 移除Worker
     * @param {string} name - Worker名称
     */
    removeWorker(name) {
        const workerInfo = this.workers.get(name);
        if (workerInfo) {
            workerInfo.worker.terminate();
            this.workers.delete(name);
        }
    }
    
    /**
     * 生成任务ID
     * @returns {string}
     */
    generateTaskId() {
        return `task-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
    }
    
    /**
     * 清理所有Worker
     */
    cleanup() {
        for (const [name] of this.workers) {
            this.removeWorker(name);
        }
    }
}

// Worker脚本示例 (data-processor.js)
const workerScript = `
self.onmessage = function(event) {
    const { taskId, type, data } = event.data;
    
    try {
        let result;
        
        switch (type) {
            case 'processData':
                result = processLargeDataSet(data);
                break;
            case 'imageFilter':
                result = applyImageFilter(data);
                break;
            case 'calculation':
                result = performComplexCalculation(data);
                break;
            default:
                throw new Error('Unknown task type: ' + type);
        }
        
        self.postMessage({ taskId, result });
    } catch (error) {
        self.postMessage({ taskId, error: error.message });
    }
};

function processLargeDataSet(data) {
    return data.map(item => {
        // 复杂数据处理逻辑
        return {
            ...item,
            processed: true,
            timestamp: Date.now()
        };
    });
}

function applyImageFilter(imageData) {
    const { data: pixels, width, height } = imageData;
    
    // 应用灰度滤镜
    for (let i = 0; i < pixels.length; i += 4) {
        const gray = pixels[i] * 0.299 + pixels[i + 1] * 0.587 + pixels[i + 2] * 0.114;
        pixels[i] = gray;     // Red
        pixels[i + 1] = gray; // Green
        pixels[i + 2] = gray; // Blue
        // Alpha channel (i + 3) remains unchanged
    }
    
    return { data: pixels, width, height };
}

function performComplexCalculation(numbers) {
    return numbers.reduce((acc, num) => {
        // 模拟复杂计算
        for (let i = 0; i < 1000000; i++) {
            acc += Math.sqrt(num * i);
        }
        return acc;
    }, 0);
}
`;

// 使用示例
const workerManager = new WorkerManager();

// 创建数据处理Worker
await workerManager.createWorker('dataProcessor', 'data-processor.js');

// 处理大数据集
const largeData = Array.from({ length: 10000 }, (_, i) => ({ id: i, value: Math.random() }));

try {
    const result = await workerManager.executeTask('dataProcessor', 'processData', largeData);
    console.log('数据处理完成:', result);
    updateUI(result);
} catch (error) {
    console.error('数据处理失败:', error);
}

// 批量处理任务
const tasks = [
    { type: 'calculation', data: [1, 2, 3, 4, 5] },
    { type: 'calculation', data: [6, 7, 8, 9, 10] },
    { type: 'calculation', data: [11, 12, 13, 14, 15] }
];

const batchResults = await workerManager.processBatch(tasks, 'data-processor.js');
console.log('批量处理结果:', batchResults);

11. WebRTC接口:实时通信的核心

🔍 应用场景

视频通话、音频聊天、屏幕共享、P2P数据传输

❌ 常见问题

// ❌ 简单的WebRTC实现,缺乏错误处理和连接管理
const pc = new RTCPeerConnection();
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
    .then(stream => {
        pc.addStream(stream);
    });

✅ 推荐方案

// ✅ 专业的WebRTC通信管理器
class WebRTCManager {
    constructor(options = {}) {
        this.options = {
            iceServers: [
                { urls: 'stun:stun.l.google.com:19302' },
                { urls: 'stun:stun1.l.google.com:19302' }
            ],
            ...options
        };
        
        this.localStream = null;
        this.remoteStream = null;
        this.peerConnection = null;
        this.dataChannel = null;
        this.isInitiator = false;
        this.listeners = new Map();
    }
    
    /**
     * 初始化WebRTC连接
     * @returns {Promise}
     */
    async initialize() {
        try {
            this.peerConnection = new RTCPeerConnection({
                iceServers: this.options.iceServers
            });
            
            this.setupPeerConnectionEvents();
            return true;
        } catch (error) {
            console.error('WebRTC初始化失败:', error);
            throw error;
        }
    }
    
    /**
     * 设置PeerConnection事件监听
     */
    setupPeerConnectionEvents() {
        // ICE候选事件
        this.peerConnection.onicecandidate = (event) => {
            if (event.candidate) {
                this.emit('iceCandidate', event.candidate);
            }
        };
        
        // 连接状态变化
        this.peerConnection.onconnectionstatechange = () => {
            const state = this.peerConnection.connectionState;
            this.emit('connectionStateChange', state);
            
            if (state === 'failed') {
                this.handleConnectionFailure();
            }
        };
        
        // 远程流接收
        this.peerConnection.ontrack = (event) => {
            this.remoteStream = event.streams[0];
            this.emit('remoteStream', this.remoteStream);
        };
        
        // 数据通道接收
        this.peerConnection.ondatachannel = (event) => {
            const channel = event.channel;
            this.setupDataChannelEvents(channel);
            this.emit('dataChannel', channel);
        };
    }
    
    /**
     * 获取用户媒体
     * @param {Object} constraints - 媒体约束
     * @returns {Promise<MediaStream>}
     */
    async getUserMedia(constraints = { video: true, audio: true }) {
        try {
            this.localStream = await navigator.mediaDevices.getUserMedia(constraints);
            
            // 添加轨道到PeerConnection
            this.localStream.getTracks().forEach(track => {
                this.peerConnection.addTrack(track, this.localStream);
            });
            
            this.emit('localStream', this.localStream);
            return this.localStream;
        } catch (error) {
            console.error('获取用户媒体失败:', error);
            throw error;
        }
    }
    
    /**
     * 获取屏幕共享
     * @returns {Promise<MediaStream>}
     */
    async getDisplayMedia() {
        try {
            const screenStream = await navigator.mediaDevices.getDisplayMedia({
                video: true,
                audio: true
            });
            
            // 替换视频轨道
            const videoTrack = screenStream.getVideoTracks()[0];
            const sender = this.peerConnection.getSenders().find(s => 
                s.track && s.track.kind === 'video'
            );
            
            if (sender) {
                await sender.replaceTrack(videoTrack);
            }
            
            // 监听屏幕共享结束
            videoTrack.onended = () => {
                this.stopScreenShare();
            };
            
            this.emit('screenShare', screenStream);
            return screenStream;
        } catch (error) {
            console.error('获取屏幕共享失败:', error);
            throw error;
        }
    }
    
    /**
     * 停止屏幕共享
     */
    async stopScreenShare() {
        try {
            // 恢复摄像头
            const videoTrack = this.localStream.getVideoTracks()[0];
            const sender = this.peerConnection.getSenders().find(s => 
                s.track && s.track.kind === 'video'
            );
            
            if (sender && videoTrack) {
                await sender.replaceTrack(videoTrack);
            }
            
            this.emit('screenShareStopped');
        } catch (error) {
            console.error('停止屏幕共享失败:', error);
        }
    }
    
    /**
     * 创建Offer
     * @returns {Promise<RTCSessionDescription>}
     */
    async createOffer() {
        try {
            this.isInitiator = true;
            
            // 创建数据通道
            this.dataChannel = this.peerConnection.createDataChannel('messages', {
                ordered: true
            });
            this.setupDataChannelEvents(this.dataChannel);
            
            const offer = await this.peerConnection.createOffer();
            await this.peerConnection.setLocalDescription(offer);
            
            return offer;
        } catch (error) {
            console.error('创建Offer失败:', error);
            throw error;
        }
    }
    
    /**
     * 创建Answer
     * @param {RTCSessionDescription} offer - 远程Offer
     * @returns {Promise<RTCSessionDescription>}
     */
    async createAnswer(offer) {
        try {
            await this.peerConnection.setRemoteDescription(offer);
            
            const answer = await this.peerConnection.createAnswer();
            await this.peerConnection.setLocalDescription(answer);
            
            return answer;
        } catch (error) {
            console.error('创建Answer失败:', error);
            throw error;
        }
    }
    
    /**
     * 设置远程描述
     * @param {RTCSessionDescription} answer - 远程Answer
     */
    async setRemoteAnswer(answer) {
        try {
            await this.peerConnection.setRemoteDescription(answer);
        } catch (error) {
            console.error('设置远程Answer失败:', error);
            throw error;
        }
    }
    
    /**
     * 添加ICE候选
     * @param {RTCIceCandidate} candidate - ICE候选
     */
    async addIceCandidate(candidate) {
        try {
            await this.peerConnection.addIceCandidate(candidate);
        } catch (error) {
            console.error('添加ICE候选失败:', error);
        }
    }
    
    /**
     * 设置数据通道事件
     * @param {RTCDataChannel} channel - 数据通道
     */
    setupDataChannelEvents(channel) {
        channel.onopen = () => {
            this.emit('dataChannelOpen', channel);
        };
        
        channel.onmessage = (event) => {
            this.emit('dataChannelMessage', event.data);
        };
        
        channel.onclose = () => {
            this.emit('dataChannelClose');
        };
        
        channel.onerror = (error) => {
            console.error('数据通道错误:', error);
            this.emit('dataChannelError', error);
        };
    }
    
    /**
     * 发送数据
     * @param {*} data - 要发送的数据
     */
    sendData(data) {
        if (this.dataChannel && this.dataChannel.readyState === 'open') {
            const message = typeof data === 'string' ? data : JSON.stringify(data);
            this.dataChannel.send(message);
        } else {
            console.warn('数据通道未打开');
        }
    }
    
    /**
     * 处理连接失败
     */
    async handleConnectionFailure() {
        console.log('连接失败,尝试重新连接...');
        
        // 重新创建ICE连接
        this.peerConnection.restartIce();
        
        this.emit('connectionFailure');
    }
    
    /**
     * 关闭连接
     */
    close() {
        // 停止本地流
        if (this.localStream) {
            this.localStream.getTracks().forEach(track => track.stop());
        }
        
        // 关闭数据通道
        if (this.dataChannel) {
            this.dataChannel.close();
        }
        
        // 关闭PeerConnection
        if (this.peerConnection) {
            this.peerConnection.close();
        }
        
        this.emit('closed');
    }
    
    /**
     * 事件监听
     * @param {string} event - 事件名
     * @param {Function} callback - 回调函数
     */
    on(event, callback) {
        if (!this.listeners.has(event)) {
            this.listeners.set(event, []);
        }
        this.listeners.get(event).push(callback);
    }
    
    /**
     * 触发事件
     * @param {string} event - 事件名
     * @param {*} data - 事件数据
     */
    emit(event, data) {
        const callbacks = this.listeners.get(event) || [];
        callbacks.forEach(callback => callback(data));
    }
}

// 使用示例
const rtcManager = new WebRTCManager();

// 初始化WebRTC
await rtcManager.initialize();

// 监听事件
rtcManager.on('localStream', (stream) => {
    document.getElementById('localVideo').srcObject = stream;
});

rtcManager.on('remoteStream', (stream) => {
    document.getElementById('remoteVideo').srcObject = stream;
});

rtcManager.on('dataChannelMessage', (message) => {
    console.log('收到消息:', message);
});

// 发起通话
document.getElementById('startCall').addEventListener('click', async () => {
    await rtcManager.getUserMedia();
    const offer = await rtcManager.createOffer();
    // 通过信令服务器发送offer
    sendToSignalingServer({ type: 'offer', offer });
});

// 接听通话
document.getElementById('answerCall').addEventListener('click', async () => {
    await rtcManager.getUserMedia();
    // 假设从信令服务器收到offer
    const answer = await rtcManager.createAnswer(receivedOffer);
    // 通过信令服务器发送answer
    sendToSignalingServer({ type: 'answer', answer });
});

12. WebGL接口:3D图形渲染的强大工具

🔍 应用场景

3D游戏、数据可视化、CAD应用、虚拟现实

❌ 常见问题

// ❌ 直接使用WebGL API,代码复杂难维护
const canvas = document.getElementById('canvas');
const gl = canvas.getContext('webgl');
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);

✅ 推荐方案

// ✅ WebGL渲染引擎
class WebGLRenderer {
    constructor(canvas, options = {}) {
        this.canvas = canvas;
        this.gl = this.initWebGL();
        this.programs = new Map();
        this.buffers = new Map();
        this.textures = new Map();
        this.uniforms = new Map();
        
        this.options = {
            clearColor: [0.0, 0.0, 0.0, 1.0],
            enableDepthTest: true,
            ...options
        };
        
        this.setupWebGL();
    }
    
    /**
     * 初始化WebGL上下文
     * @returns {WebGLRenderingContext}
     */
    initWebGL() {
        const gl = this.canvas.getContext('webgl') || this.canvas.getContext('experimental-webgl');
        
        if (!gl) {
            throw new Error('WebGL not supported');
        }
        
        return gl;
    }
    
    /**
     * 设置WebGL基本配置
     */
    setupWebGL() {
        const { gl, options } = this;
        
        // 设置清除颜色
        gl.clearColor(...options.clearColor);
        
        // 启用深度测试
        if (options.enableDepthTest) {
            gl.enable(gl.DEPTH_TEST);
            gl.depthFunc(gl.LEQUAL);
        }
        
        // 设置视口
        this.resize();
    }
    
    /**
     * 创建着色器
     * @param {string} source - 着色器源码
     * @param {number} type - 着色器类型
     * @returns {WebGLShader}
     */
    createShader(source, type) {
        const { gl } = this;
        const shader = gl.createShader(type);
        
        gl.shaderSource(shader, source);
        gl.compileShader(shader);
        
        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
            const error = gl.getShaderInfoLog(shader);
            gl.deleteShader(shader);
            throw new Error(`Shader compilation error: ${error}`);
        }
        
        return shader;
    }
    
    /**
     * 创建着色器程序
     * @param {string} vertexSource - 顶点着色器源码
     * @param {string} fragmentSource - 片段着色器源码
     * @param {string} name - 程序名称
     * @returns {WebGLProgram}
     */
    createProgram(vertexSource, fragmentSource, name) {
        const { gl } = this;
        
        const vertexShader = this.createShader(vertexSource, gl.VERTEX_SHADER);
        const fragmentShader = this.createShader(fragmentSource, gl.FRAGMENT_SHADER);
        
        const program = gl.createProgram();
        gl.attachShader(program, vertexShader);
        gl.attachShader(program, fragmentShader);
        gl.linkProgram(program);
        
        if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
            const error = gl.getProgramInfoLog(program);
            gl.deleteProgram(program);
            throw new Error(`Program linking error: ${error}`);
        }
        
        // 清理着色器
        gl.deleteShader(vertexShader);
        gl.deleteShader(fragmentShader);
        
        // 存储程序
        this.programs.set(name, program);
        
        return program;
    }
    
    /**
     * 创建缓冲区
     * @param {ArrayBuffer|Float32Array} data - 数据
     * @param {number} type - 缓冲区类型
     * @param {number} usage - 使用方式
     * @param {string} name - 缓冲区名称
     * @returns {WebGLBuffer}
     */
    createBuffer(data, type = this.gl.ARRAY_BUFFER, usage = this.gl.STATIC_DRAW, name) {
        const { gl } = this;
        const buffer = gl.createBuffer();
        
        gl.bindBuffer(type, buffer);
        gl.bufferData(type, data, usage);
        
        if (name) {
            this.buffers.set(name, { buffer, type, size: data.length });
        }
        
        return buffer;
    }
    
    /**
     * 创建纹理
     * @param {HTMLImageElement|HTMLCanvasElement} image - 图像源
     * @param {string} name - 纹理名称
     * @returns {WebGLTexture}
     */
    createTexture(image, name) {
        const { gl } = this;
        const texture = gl.createTexture();
        
        gl.bindTexture(gl.TEXTURE_2D, texture);
        
        // 设置纹理参数
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        
        // 上传纹理数据
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
        
        if (name) {
            this.textures.set(name, texture);
        }
        
        return texture;
    }
    
    /**
     * 设置uniform变量
     * @param {WebGLProgram} program - 着色器程序
     * @param {string} name - uniform名称
     * @param {*} value - 值
     */
    setUniform(program, name, value) {
        const { gl } = this;
        const location = gl.getUniformLocation(program, name);
        
        if (location === null) return;
        
        if (Array.isArray(value)) {
            switch (value.length) {
                case 1:
                    gl.uniform1f(location, value[0]);
                    break;
                case 2:
                    gl.uniform2fv(location, value);
                    break;
                case 3:
                    gl.uniform3fv(location, value);
                    break;
                case 4:
                    gl.uniform4fv(location, value);
                    break;
                case 16:
                    gl.uniformMatrix4fv(location, false, value);
                    break;
            }
        } else if (typeof value === 'number') {
            gl.uniform1f(location, value);
        }
    }
    
    /**
     * 绑定属性
     * @param {WebGLProgram} program - 着色器程序
     * @param {string} name - 属性名称
     * @param {WebGLBuffer} buffer - 缓冲区
     * @param {number} size - 组件数量
     * @param {number} type - 数据类型
     */
    bindAttribute(program, name, buffer, size = 3, type = this.gl.FLOAT) {
        const { gl } = this;
        const location = gl.getAttribLocation(program, name);
        
        if (location === -1) return;
        
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
        gl.enableVertexAttribArray(location);
        gl.vertexAttribPointer(location, size, type, false, 0, 0);
    }
    
    /**
     * 渲染场景
     * @param {Object} scene - 场景对象
     */
    render(scene) {
        const { gl } = this;
        
        // 清除画布
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        
        // 渲染每个对象
        scene.objects.forEach(object => {
            this.renderObject(object);
        });
    }
    
    /**
     * 渲染单个对象
     * @param {Object} object - 渲染对象
     */
    renderObject(object) {
        const { gl } = this;
        const program = this.programs.get(object.program);
        
        if (!program) return;
        
        // 使用着色器程序
        gl.useProgram(program);
        
        // 设置uniforms
        Object.entries(object.uniforms || {}).forEach(([name, value]) => {
            this.setUniform(program, name, value);
        });
        
        // 绑定属性
        Object.entries(object.attributes || {}).forEach(([name, attr]) => {
            const buffer = this.buffers.get(attr.buffer);
            if (buffer) {
                this.bindAttribute(program, name, buffer.buffer, attr.size, attr.type);
            }
        });
        
        // 绑定纹理
        if (object.texture) {
            const texture = this.textures.get(object.texture);
            if (texture) {
                gl.activeTexture(gl.TEXTURE0);
                gl.bindTexture(gl.TEXTURE_2D, texture);
                this.setUniform(program, 'u_texture', 0);
            }
        }
        
        // 绘制
        if (object.indices) {
            const indexBuffer = this.buffers.get(object.indices);
            if (indexBuffer) {
                gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer.buffer);
                gl.drawElements(object.mode || gl.TRIANGLES, indexBuffer.size, gl.UNSIGNED_SHORT, 0);
            }
        } else {
            const vertexBuffer = this.buffers.get(object.vertices);
            if (vertexBuffer) {
                gl.drawArrays(object.mode || gl.TRIANGLES, 0, vertexBuffer.size / 3);
            }
        }
    }
    
    /**
     * 调整画布大小
     */
    resize() {
        const { canvas, gl } = this;
        const displayWidth = canvas.clientWidth;
        const displayHeight = canvas.clientHeight;
        
        if (canvas.width !== displayWidth || canvas.height !== displayHeight) {
            canvas.width = displayWidth;
            canvas.height = displayHeight;
            gl.viewport(0, 0, displayWidth, displayHeight);
        }
    }
    
    /**
     * 清理资源
     */
    cleanup() {
        const { gl } = this;
        
        // 删除程序
        this.programs.forEach(program => gl.deleteProgram(program));
        
        // 删除缓冲区
        this.buffers.forEach(({ buffer }) => gl.deleteBuffer(buffer));
        
        // 删除纹理
        this.textures.forEach(texture => gl.deleteTexture(texture));
    }
}

// 使用示例
const canvas = document.getElementById('webgl-canvas');
const renderer = new WebGLRenderer(canvas);

// 顶点着色器
const vertexShaderSource = `
    attribute vec3 a_position;
    attribute vec2 a_texCoord;
    
    uniform mat4 u_modelViewMatrix;
    uniform mat4 u_projectionMatrix;
    
    varying vec2 v_texCoord;
    
    void main() {
        gl_Position = u_projectionMatrix * u_modelViewMatrix * vec4(a_position, 1.0);
        v_texCoord = a_texCoord;
    }
`;

// 片段着色器
const fragmentShaderSource = `
    precision mediump float;
    
    uniform sampler2D u_texture;
    uniform float u_time;
    
    varying vec2 v_texCoord;
    
    void main() {
        vec4 color = texture2D(u_texture, v_texCoord);
        color.rgb *= 0.5 + 0.5 * sin(u_time);
        gl_FragColor = color;
    }
`;

// 创建着色器程序
renderer.createProgram(vertexShaderSource, fragmentShaderSource, 'basic');

// 创建立方体顶点数据
const vertices = new Float32Array([
    // 前面
    -1, -1,  1,
     1, -1,  1,
     1,  1,  1,
    -1,  1,  1,
    // 后面
    -1, -1, -1,
    -1,  1, -1,
     1,  1, -1,
     1, -1, -1
]);

const indices = new Uint16Array([
    0, 1, 2,   0, 2, 3,    // 前面
    4, 5, 6,   4, 6, 7,    // 后面
    5, 0, 3,   5, 3, 6,    // 左面
    1, 4, 7,   1, 7, 2,    // 右面
    3, 2, 7,   3, 7, 6,    // 上面
    5, 4, 1,   5, 1, 0     // 下面
]);

// 创建缓冲区
renderer.createBuffer(vertices, renderer.gl.ARRAY_BUFFER, renderer.gl.STATIC_DRAW, 'vertices');
renderer.createBuffer(indices, renderer.gl.ELEMENT_ARRAY_BUFFER, renderer.gl.STATIC_DRAW, 'indices');

// 创建场景
const scene = {
    objects: [{
        program: 'basic',
        vertices: 'vertices',
        indices: 'indices',
        attributes: {
            a_position: { buffer: 'vertices', size: 3 }
        },
        uniforms: {
            u_modelViewMatrix: [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,-5,1],
            u_projectionMatrix: [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1],
            u_time: 0
        }
    }]
};

// 渲染循环
function animate(time) {
    scene.objects[0].uniforms.u_time = time * 0.001;
    renderer.render(scene);
    requestAnimationFrame(animate);
}

animate(0);

13. Web Audio接口:音频处理的专业方案

🔍 应用场景

音频播放器、音效处理、音乐制作、语音识别

❌ 常见问题

// ❌ 简单的音频播放,功能有限
const audio = new Audio('music.mp3');
audio.play();
audio.volume = 0.5;

✅ 推荐方案

// ✅ 专业音频处理引擎
class AudioEngine {
    constructor() {
        this.context = null;
        this.masterGain = null;
        this.sources = new Map();
        this.effects = new Map();
        this.isInitialized = false;
    }
    
    /**
     * 初始化音频上下文
     * @returns {Promise}
     */
    async initialize() {
        try {
            // 创建音频上下文
            this.context = new (window.AudioContext || window.webkitAudioContext)();
            
            // 创建主音量控制
            this.masterGain = this.context.createGain();
            this.masterGain.connect(this.context.destination);
            
            // 恢复音频上下文(某些浏览器需要用户交互)
            if (this.context.state === 'suspended') {
                await this.context.resume();
            }
            
            this.isInitialized = true;
            console.log('音频引擎初始化成功');
        } catch (error) {
            console.error('音频引擎初始化失败:', error);
            throw error;
        }
    }
    
    /**
     * 加载音频文件
     * @param {string} url - 音频文件URL
     * @param {string} name - 音频名称
     * @returns {Promise<AudioBuffer>}
     */
    async loadAudio(url, name) {
        try {
            const response = await fetch(url);
            const arrayBuffer = await response.arrayBuffer();
            const audioBuffer = await this.context.decodeAudioData(arrayBuffer);
            
            this.sources.set(name, audioBuffer);
            return audioBuffer;
        } catch (error) {
            console.error(`加载音频失败 ${url}:`, error);
            throw error;
        }
    }
    
    /**
     * 播放音频
     * @param {string} name - 音频名称
     * @param {Object} options - 播放选项
     * @returns {AudioBufferSourceNode}
     */
    playAudio(name, options = {}) {
        if (!this.isInitialized) {
            console.warn('音频引擎未初始化');
            return null;
        }
        
        const audioBuffer = this.sources.get(name);
        if (!audioBuffer) {
            console.warn(`音频 ${name} 未找到`);
            return null;
        }
        
        const source = this.context.createBufferSource();
        source.buffer = audioBuffer;
        
        // 创建音量控制
        const gainNode = this.context.createGain();
        gainNode.gain.value = options.volume || 1.0;
        
        // 连接音频图
        source.connect(gainNode);
        gainNode.connect(this.masterGain);
        
        // 设置播放参数
        if (options.loop) {
            source.loop = true;
        }
        
        if (options.playbackRate) {
            source.playbackRate.value = options.playbackRate;
        }
        
        // 开始播放
        const startTime = options.when || this.context.currentTime;
        const offset = options.offset || 0;
        const duration = options.duration || audioBuffer.duration;
        
        source.start(startTime, offset, duration);
        
        // 设置结束回调
        if (options.onEnded) {
            source.onended = options.onEnded;
        }
        
        return source;
    }
    
    /**
     * 创建音频效果器
     * @param {string} type - 效果器类型
     * @param {Object} params - 参数
     * @returns {AudioNode}
     */
    createEffect(type, params = {}) {
        let effect;
        
        switch (type) {
            case 'reverb':
                effect = this.createReverb(params);
                break;
            case 'delay':
                effect = this.createDelay(params);
                break;
            case 'filter':
                effect = this.createFilter(params);
                break;
            case 'distortion':
                effect = this.createDistortion(params);
                break;
            case 'compressor':
                effect = this.createCompressor(params);
                break;
            default:
                console.warn(`未知效果器类型: ${type}`);
                return null;
        }
        
        return effect;
    }
    
    /**
     * 创建混响效果
     * @param {Object} params - 混响参数
     * @returns {ConvolverNode}
     */
    createReverb(params = {}) {
        const convolver = this.context.createConvolver();
        
        // 创建冲激响应
        const length = params.length || this.context.sampleRate * 2;
        const impulse = this.context.createBuffer(2, length, this.context.sampleRate);
        
        for (let channel = 0; channel < 2; channel++) {
            const channelData = impulse.getChannelData(channel);
            for (let i = 0; i < length; i++) {
                const decay = Math.pow(1 - i / length, params.decay || 2);
                channelData[i] = (Math.random() * 2 - 1) * decay;
            }
        }
        
        convolver.buffer = impulse;
        return convolver;
    }
    
    /**
     * 创建延迟效果
     * @param {Object} params - 延迟参数
     * @returns {Object}
     */
    createDelay(params = {}) {
        const delay = this.context.createDelay(params.maxDelay || 1.0);
        const feedback = this.context.createGain();
        const wetGain = this.context.createGain();
        const dryGain = this.context.createGain();
        const output = this.context.createGain();
        
        // 设置参数
        delay.delayTime.value = params.delayTime || 0.3;
        feedback.gain.value = params.feedback || 0.3;
        wetGain.gain.value = params.wet || 0.5;
        dryGain.gain.value = params.dry || 0.5;
        
        // 连接节点
        delay.connect(feedback);
        feedback.connect(delay);
        delay.connect(wetGain);
        wetGain.connect(output);
        dryGain.connect(output);
        
        return {
            input: delay,
            output: output,
            dryGain: dryGain
        };
    }
    
    /**
     * 创建滤波器
     * @param {Object} params - 滤波器参数
     * @returns {BiquadFilterNode}
     */
    createFilter(params = {}) {
        const filter = this.context.createBiquadFilter();
        
        filter.type = params.type || 'lowpass';
        filter.frequency.value = params.frequency || 1000;
        filter.Q.value = params.Q || 1;
        filter.gain.value = params.gain || 0;
        
        return filter;
    }
    
    /**
     * 创建失真效果
     * @param {Object} params - 失真参数
     * @returns {WaveShaperNode}
     */
    createDistortion(params = {}) {
        const waveshaper = this.context.createWaveShaper();
        const amount = params.amount || 50;
        const samples = 44100;
        const curve = new Float32Array(samples);
        
        for (let i = 0; i < samples; i++) {
            const x = (i * 2) / samples - 1;
            curve[i] = ((3 + amount) * x * 20 * Math.PI / 180) / (Math.PI + amount * Math.abs(x));
        }
        
        waveshaper.curve = curve;
        waveshaper.oversample = '4x';
        
        return waveshaper;
    }
    
    /**
     * 创建压缩器
     * @param {Object} params - 压缩器参数
     * @returns {DynamicsCompressorNode}
     */
    createCompressor(params = {}) {
        const compressor = this.context.createDynamicsCompressor();
        
        compressor.threshold.value = params.threshold || -24;
        compressor.knee.value = params.knee || 30;
        compressor.ratio.value = params.ratio || 12;
        compressor.attack.value = params.attack || 0.003;
        compressor.release.value = params.release || 0.25;
        
        return compressor;
    }
    
    /**
     * 创建音频分析器
     * @param {number} fftSize - FFT大小
     * @returns {AnalyserNode}
     */
    createAnalyser(fftSize = 2048) {
        const analyser = this.context.createAnalyser();
        analyser.fftSize = fftSize;
        analyser.smoothingTimeConstant = 0.8;
        
        return analyser;
    }
    
    /**
     * 录制音频
     * @param {MediaStream} stream - 媒体流
     * @returns {Object}
     */
    createRecorder(stream) {
        const source = this.context.createMediaStreamSource(stream);
        const processor = this.context.createScriptProcessor(4096, 1, 1);
        const recordedChunks = [];
        
        let isRecording = false;
        
        processor.onaudioprocess = (event) => {
            if (!isRecording) return;
            
            const inputData = event.inputBuffer.getChannelData(0);
            const chunk = new Float32Array(inputData);
            recordedChunks.push(chunk);
        };
        
        source.connect(processor);
        processor.connect(this.context.destination);
        
        return {
            start: () => {
                isRecording = true;
                recordedChunks.length = 0;
            },
            stop: () => {
                isRecording = false;
                return this.exportRecording(recordedChunks);
            },
            source: source
        };
    }
    
    /**
     * 导出录音
     * @param {Array} chunks - 音频块
     * @returns {AudioBuffer}
     */
    exportRecording(chunks) {
        const totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0);
        const audioBuffer = this.context.createBuffer(1, totalLength, this.context.sampleRate);
        const channelData = audioBuffer.getChannelData(0);
        
        let offset = 0;
        chunks.forEach(chunk => {
            channelData.set(chunk, offset);
            offset += chunk.length;
        });
        
        return audioBuffer;
    }
    
    /**
     * 设置主音量
     * @param {number} volume - 音量值 (0-1)
     */
    setMasterVolume(volume) {
        if (this.masterGain) {
            this.masterGain.gain.value = Math.max(0, Math.min(1, volume));
        }
    }
    
    /**
     * 获取当前时间
     * @returns {number}
     */
    getCurrentTime() {
        return this.context ? this.context.currentTime : 0;
    }
    
    /**
     * 暂停音频上下文
     */
    suspend() {
        if (this.context && this.context.state === 'running') {
            return this.context.suspend();
        }
    }
    
    /**
     * 恢复音频上下文
     */
    resume() {
        if (this.context && this.context.state === 'suspended') {
            return this.context.resume();
        }
    }
}

// 使用示例
const audioEngine = new AudioEngine();

// 初始化音频引擎
await audioEngine.initialize();

// 加载音频文件
await audioEngine.loadAudio('music.mp3', 'bgm');
await audioEngine.loadAudio('click.wav', 'click');

// 播放背景音乐
const bgmSource = audioEngine.playAudio('bgm', {
    loop: true,
    volume: 0.7
});

// 播放点击音效
document.getElementById('button').addEventListener('click', () => {
    audioEngine.playAudio('click', {
        volume: 0.5,
        playbackRate: 1.2
    });
});

// 创建音频效果链
const reverb = audioEngine.createEffect('reverb', {
    length: audioEngine.context.sampleRate * 3,
    decay: 2
});

const delay = audioEngine.createEffect('delay', {
    delayTime: 0.3,
    feedback: 0.4,
    wet: 0.3
});

// 连接效果链
bgmSource.disconnect();
bgmSource.connect(reverb);
reverb.connect(delay.input);
delay.output.connect(audioEngine.masterGain);
### 14. 触摸手势接口:移动端交互的基础

#### 🔍 应用场景
移动端手势识别、触摸交互、手势控制、多点触控

#### ❌ 常见问题
```javascript
// ❌ 简单的触摸事件处理,功能有限
element.addEventListener('touchstart', (e) => {
    console.log('触摸开始');
});

✅ 推荐方案

// ✅ 专业的触摸手势管理器
class TouchGestureManager {
    constructor(element, options = {}) {
        this.element = element;
        this.options = {
            enableTap: true,
            enableSwipe: true,
            enablePinch: true,
            enableRotate: true,
            enableLongPress: true,
            tapTimeout: 300,
            longPressTimeout: 500,
            swipeThreshold: 50,
            ...options
        };
        
        this.touches = new Map();
        this.gestureState = {
            isActive: false,
            startTime: 0,
            startDistance: 0,
            startAngle: 0,
            lastScale: 1,
            lastRotation: 0
        };
        
        this.listeners = new Map();
        this.longPressTimer = null;
        this.mouseDown = false;
        
        this.init();
    }
    
    /**
     * 初始化事件监听
     */
    init() {
        // 触摸事件
        this.handleTouchStart = this.handleTouchStart.bind(this);
        this.handleTouchMove = this.handleTouchMove.bind(this);
        this.handleTouchEnd = this.handleTouchEnd.bind(this);
        this.handleTouchCancel = this.handleTouchCancel.bind(this);
        
        this.element.addEventListener('touchstart', this.handleTouchStart, { passive: false });
        this.element.addEventListener('touchmove', this.handleTouchMove, { passive: false });
        this.element.addEventListener('touchend', this.handleTouchEnd, { passive: false });
        this.element.addEventListener('touchcancel', this.handleTouchCancel, { passive: false });
        
        // 鼠标事件(用于桌面端测试)
        this.handleMouseDown = this.handleMouseDown.bind(this);
        this.handleMouseMove = this.handleMouseMove.bind(this);
        this.handleMouseUp = this.handleMouseUp.bind(this);
        
        this.element.addEventListener('mousedown', this.handleMouseDown);
        document.addEventListener('mousemove', this.handleMouseMove);
        document.addEventListener('mouseup', this.handleMouseUp);
    }
    
    /**
     * 处理触摸开始
     * @param {TouchEvent} event - 触摸事件
     */
    handleTouchStart(event) {
        event.preventDefault();
        
        const touches = Array.from(event.touches);
        this.updateTouches(touches);
        
        if (touches.length === 1) {
            // 单点触摸
            const touch = touches[0];
            this.gestureState.startX = touch.clientX;
            this.gestureState.startY = touch.clientY;
            this.gestureState.startTime = Date.now();
            
            // 长按检测
            if (this.options.enableLongPress) {
                this.longPressTimer = setTimeout(() => {
                    this.emit('longpress', {
                        x: touch.clientX,
                        y: touch.clientY,
                        touch: touch
                    });
                }, this.options.longPressTimeout);
            }
        } else if (touches.length === 2) {
            // 双点触摸
            this.gestureState.isActive = true;
            this.gestureState.startDistance = this.getDistance(touches[0], touches[1]);
            this.gestureState.startAngle = this.getAngle(touches[0], touches[1]);
            this.gestureState.lastScale = 1;
            this.gestureState.lastRotation = 0;
            
            // 清除长按定时器
            if (this.longPressTimer) {
                clearTimeout(this.longPressTimer);
                this.longPressTimer = null;
            }
        }
    }
    
    /**
     * 处理触摸移动
     * @param {TouchEvent} event - 触摸事件
     */
    handleTouchMove(event) {
        event.preventDefault();
        
        const touches = Array.from(event.touches);
        this.updateTouches(touches);
        
        if (touches.length === 2 && this.gestureState.isActive) {
            const currentDistance = this.getDistance(touches[0], touches[1]);
            const currentAngle = this.getAngle(touches[0], touches[1]);
            const center = this.getCenter(touches[0], touches[1]);
            
            // 缩放检测
            if (this.options.enablePinch) {
                const scale = currentDistance / this.gestureState.startDistance;
                const deltaScale = scale / this.gestureState.lastScale;
                
                this.emit('pinch', {
                    scale: scale,
                    deltaScale: deltaScale,
                    center: center,
                    touches: touches
                });
                
                this.gestureState.lastScale = scale;
            }
            
            // 旋转检测
            if (this.options.enableRotate) {
                let rotation = currentAngle - this.gestureState.startAngle;
                
                // 处理角度跨越
                if (rotation > 180) rotation -= 360;
                if (rotation < -180) rotation += 360;
                
                const deltaRotation = rotation - this.gestureState.lastRotation;
                
                this.emit('rotate', {
                    rotation: rotation,
                    deltaRotation: deltaRotation,
                    center: center,
                    touches: touches
                });
                
                this.gestureState.lastRotation = rotation;
            }
        }
        
        // 清除长按定时器(移动时取消长按)
        if (this.longPressTimer) {
            clearTimeout(this.longPressTimer);
            this.longPressTimer = null;
        }
    }
    
    /**
     * 处理触摸结束
     * @param {TouchEvent} event - 触摸事件
     */
    handleTouchEnd(event) {
        event.preventDefault();
        
        const changedTouches = Array.from(event.changedTouches);
        const remainingTouches = Array.from(event.touches);
        
        this.updateTouches(remainingTouches);
        
        // 重置手势状态
        if (remainingTouches.length === 0) {
            this.gestureState.isActive = false;
        }
        
        // 单点手势检测
        const duration = Date.now() - this.gestureState.startTime;
        
        if (changedTouches.length === 1 && duration < this.options.tapTimeout) {
            const touch = changedTouches[0];
            const deltaX = Math.abs(touch.clientX - this.gestureState.startX);
            const deltaY = Math.abs(touch.clientY - this.gestureState.startY);
            
            // 点击检测
            if (this.options.enableTap && deltaX < 10 && deltaY < 10) {
                this.emit('tap', {
                    x: touch.clientX,
                    y: touch.clientY,
                    touch: touch
                });
            }
            
            // 滑动检测
            if (this.options.enableSwipe && (deltaX > this.options.swipeThreshold || deltaY > this.options.swipeThreshold)) {
                let direction;
                if (deltaX > deltaY) {
                    direction = touch.clientX > this.gestureState.startX ? 'right' : 'left';
                } else {
                    direction = touch.clientY > this.gestureState.startY ? 'down' : 'up';
                }
                
                this.emit('swipe', {
                    direction: direction,
                    deltaX: touch.clientX - this.gestureState.startX,
                    deltaY: touch.clientY - this.gestureState.startY,
                    velocity: Math.sqrt(deltaX * deltaX + deltaY * deltaY) / duration,
                    touch: touch
                });
            }
        }
        
        // 清除长按定时器
        if (this.longPressTimer) {
            clearTimeout(this.longPressTimer);
            this.longPressTimer = null;
        }
    }
    
    /**
     * 处理触摸取消
     * @param {TouchEvent} event - 触摸事件
     */
    handleTouchCancel(event) {
        this.handleTouchEnd(event);
    }
    
    /**
     * 更新触摸点
     * @param {Array} touches - 触摸点数组
     */
    updateTouches(touches) {
        this.touches.clear();
        touches.forEach(touch => {
            this.touches.set(touch.identifier, {
                x: touch.clientX,
                y: touch.clientY,
                timestamp: Date.now()
            });
        });
    }
    
    /**
     * 获取两点间距离
     * @param {Touch} touch1 - 触摸点1
     * @param {Touch} touch2 - 触摸点2
     * @returns {number}
     */
    getDistance(touch1, touch2) {
        const dx = touch2.clientX - touch1.clientX;
        const dy = touch2.clientY - touch1.clientY;
        return Math.sqrt(dx * dx + dy * dy);
    }
    
    /**
     * 获取两点间角度
     * @param {Touch} touch1 - 触摸点1
     * @param {Touch} touch2 - 触摸点2
     * @returns {number}
     */
    getAngle(touch1, touch2) {
        const dx = touch2.clientX - touch1.clientX;
        const dy = touch2.clientY - touch1.clientY;
        return Math.atan2(dy, dx) * 180 / Math.PI;
    }
    
    /**
     * 获取两点中心
     * @param {Touch} touch1 - 触摸点1
     * @param {Touch} touch2 - 触摸点2
     * @returns {Object}
     */
    getCenter(touch1, touch2) {
        return {
            x: (touch1.clientX + touch2.clientX) / 2,
            y: (touch1.clientY + touch2.clientY) / 2
        };
    }
    
    /**
     * 重置手势状态
     */
    resetGestureState() {
        this.gestureState = {
            isActive: false,
            startTime: 0,
            startDistance: 0,
            startAngle: 0,
            lastScale: 1,
            lastRotation: 0
        };
    }
    
    /**
     * 鼠标事件处理(桌面端测试)
     */
    handleMouseDown(event) {
        this.mouseDown = true;
        this.handleTouchStart({
            preventDefault: () => event.preventDefault(),
            touches: [{
                identifier: 0,
                clientX: event.clientX,
                clientY: event.clientY
            }]
        });
    }
    
    handleMouseMove(event) {
        if (!this.mouseDown) return;
        this.handleTouchMove({
            preventDefault: () => event.preventDefault(),
            touches: [{
                identifier: 0,
                clientX: event.clientX,
                clientY: event.clientY
            }]
        });
    }
    
    handleMouseUp(event) {
        if (!this.mouseDown) return;
        this.mouseDown = false;
        this.handleTouchEnd({
            preventDefault: () => event.preventDefault(),
            touches: [],
            changedTouches: [{
                identifier: 0,
                clientX: event.clientX,
                clientY: event.clientY
            }]
        });
    }
    
    /**
     * 事件监听
     * @param {string} event - 事件名
     * @param {Function} callback - 回调函数
     */
    on(event, callback) {
        if (!this.listeners.has(event)) {
            this.listeners.set(event, []);
        }
        this.listeners.get(event).push(callback);
    }
    
    /**
     * 移除事件监听
     * @param {string} event - 事件名
     * @param {Function} callback - 回调函数
     */
    off(event, callback) {
        const callbacks = this.listeners.get(event) || [];
        const index = callbacks.indexOf(callback);
        if (index > -1) {
            callbacks.splice(index, 1);
        }
    }
    
    /**
     * 触发事件
     * @param {string} event - 事件名
     * @param {*} data - 事件数据
     */
    emit(event, data) {
        const callbacks = this.listeners.get(event) || [];
        callbacks.forEach(callback => callback(data));
    }
    
    /**
     * 销毁手势管理器
     */
    destroy() {
        // 移除事件监听器
        this.element.removeEventListener('touchstart', this.handleTouchStart);
        this.element.removeEventListener('touchmove', this.handleTouchMove);
        this.element.removeEventListener('touchend', this.handleTouchEnd);
        this.element.removeEventListener('touchcancel', this.handleTouchCancel);
        
        this.element.removeEventListener('mousedown', this.handleMouseDown);
        this.element.removeEventListener('mousemove', this.handleMouseMove);
        this.element.removeEventListener('mouseup', this.handleMouseUp);
        
        // 清理定时器
        if (this.longPressTimer) {
            clearTimeout(this.longPressTimer);
        }
        
        // 清理数据
        this.touches.clear();
        this.listeners.clear();
    }
}

// 使用示例
const gestureElement = document.getElementById('gesture-area');
const gestureManager = new TouchGestureManager(gestureElement);

// 监听手势事件
gestureManager.on('tap', (data) => {
    console.log('点击:', data);
});

gestureManager.on('swipe', (data) => {
    console.log('滑动:', data.direction, data.velocity);
});

gestureManager.on('pinch', (data) => {
    console.log('缩放:', data.scale);
    gestureElement.style.transform = `scale(${data.scale})`;
});

gestureManager.on('rotate', (data) => {
    console.log('旋转:', data.rotation);
    gestureElement.style.transform = `rotate(${data.rotation}deg)`;
});

gestureManager.on('longpress', (data) => {
    console.log('长按:', data);
});

15. 地理位置接口:位置服务的核心

🔍 应用场景

地图应用、位置签到、导航服务、基于位置的推荐

❌ 常见问题

// ❌ 简单的位置获取,缺乏错误处理和优化
navigator.geolocation.getCurrentPosition((position) => {
    console.log(position.coords.latitude, position.coords.longitude);
});

✅ 推荐方案

// ✅ 专业的地理位置管理器
class GeolocationManager {
    constructor(options = {}) {
        this.options = {
            enableHighAccuracy: true,
            timeout: 10000,
            maximumAge: 300000, // 5分钟缓存
            ...options
        };
        
        this.currentPosition = null;
        this.watchId = null;
        this.listeners = new Map();
        this.isSupported = 'geolocation' in navigator;
    }
    
    /**
     * 检查地理位置支持
     * @returns {boolean}
     */
    isGeolocationSupported() {
        return this.isSupported;
    }
    
    /**
     * 获取当前位置
     * @param {Object} options - 选项
     * @returns {Promise<Position>}
     */
    async getCurrentPosition(options = {}) {
        if (!this.isSupported) {
            throw new Error('Geolocation is not supported');
        }
        
        const finalOptions = { ...this.options, ...options };
        
        return new Promise((resolve, reject) => {
            navigator.geolocation.getCurrentPosition(
                (position) => {
                    this.currentPosition = position;
                    this.emit('positionUpdate', position);
                    resolve(position);
                },
                (error) => {
                    this.handleError(error);
                    reject(error);
                },
                finalOptions
            );
        });
    }
    
    /**
     * 开始监听位置变化
     * @param {Object} options - 选项
     * @returns {number} watchId
     */
    startWatching(options = {}) {
        if (!this.isSupported) {
            throw new Error('Geolocation is not supported');
        }
        
        if (this.watchId !== null) {
            this.stopWatching();
        }
        
        const finalOptions = { ...this.options, ...options };
        
        this.watchId = navigator.geolocation.watchPosition(
            (position) => {
                this.currentPosition = position;
                this.emit('positionUpdate', position);
            },
            (error) => {
                this.handleError(error);
            },
            finalOptions
        );
        
        return this.watchId;
    }
    
    /**
     * 停止监听位置变化
     */
    stopWatching() {
        if (this.watchId !== null) {
            navigator.geolocation.clearWatch(this.watchId);
            this.watchId = null;
            this.emit('watchStopped');
        }
    }
    
    /**
     * 计算两点间距离
     * @param {Object} pos1 - 位置1
     * @param {Object} pos2 - 位置2
     * @returns {number} 距离(米)
     */
    calculateDistance(pos1, pos2) {
        const R = 6371e3; // 地球半径(米)
        const φ1 = pos1.latitude * Math.PI / 180;
        const φ2 = pos2.latitude * Math.PI / 180;
        const Δφ = (pos2.latitude - pos1.latitude) * Math.PI / 180;
        const Δλ = (pos2.longitude - pos1.longitude) * Math.PI / 180;
        
        const a = Math.sin(Δφ/2) * Math.sin(Δφ/2) +
                  Math.cos1) * Math.cos2) *
                  Math.sin(Δλ/2) * Math.sin(Δλ/2);
        const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
        
        return R * c;
    }
    
    /**
     * 处理地理位置错误
     * @param {GeolocationPositionError} error - 错误对象
     */
    handleError(error) {
        let message;
        
        switch (error.code) {
            case error.PERMISSION_DENIED:
                message = '用户拒绝了地理位置请求';
                break;
            case error.POSITION_UNAVAILABLE:
                message = '位置信息不可用';
                break;
            case error.TIMEOUT:
                message = '获取位置信息超时';
                break;
            default:
                message = '获取位置信息时发生未知错误';
                break;
        }
        
        this.emit('error', { code: error.code, message });
    }
    
    /**
     * 事件监听
     * @param {string} event - 事件名
     * @param {Function} callback - 回调函数
     */
    on(event, callback) {
        if (!this.listeners.has(event)) {
            this.listeners.set(event, []);
        }
        this.listeners.get(event).push(callback);
    }
    
    /**
     * 触发事件
     * @param {string} event - 事件名
     * @param {*} data - 事件数据
     */
    emit(event, data) {
        const callbacks = this.listeners.get(event) || [];
        callbacks.forEach(callback => callback(data));
    }
}

// 使用示例
const geoManager = new GeolocationManager({
    enableHighAccuracy: true,
    timeout: 15000,
    maximumAge: 600000
});

// 检查支持
if (geoManager.isGeolocationSupported()) {
    // 获取当前位置
    try {
        const position = await geoManager.getCurrentPosition();
        console.log('当前位置:', position.coords);
    } catch (error) {
        console.error('获取位置失败:', error);
    }
    
    // 监听位置变化
    geoManager.on('positionUpdate', (position) => {
        console.log('位置更新:', position.coords);
        updateMap(position.coords);
    });
    
    geoManager.startWatching();
} else {
     console.log('浏览器不支持地理位置');
 }

16. 设备方向接口:感知设备状态

🔍 应用场景

移动端游戏、AR应用、设备姿态检测、重力感应

❌ 常见问题

// ❌ 简单的设备方向监听,缺乏数据处理
window.addEventListener('deviceorientation', (event) => {
    console.log(event.alpha, event.beta, event.gamma);
});

✅ 推荐方案

// ✅ 专业的设备方向管理器
class DeviceOrientationManager {
    constructor(options = {}) {
        this.options = {
            enableSmoothing: true,
            smoothingFactor: 0.8,
            threshold: 1, // 度数阈值
            ...options
        };
        
        this.isSupported = 'DeviceOrientationEvent' in window;
        this.isListening = false;
        this.listeners = new Map();
        
        this.currentOrientation = {
            alpha: 0,   // Z轴旋转
            beta: 0,    // X轴旋转
            gamma: 0,   // Y轴旋转
            absolute: false
        };
        
        this.smoothedOrientation = { ...this.currentOrientation };
        this.lastOrientation = { ...this.currentOrientation };
    }
    
    /**
     * 检查设备方向支持
     * @returns {boolean}
     */
    isDeviceOrientationSupported() {
        return this.isSupported;
    }
    
    /**
     * 请求权限(iOS 13+需要)
     * @returns {Promise<boolean>}
     */
    async requestPermission() {
        if (typeof DeviceOrientationEvent.requestPermission === 'function') {
            try {
                const permission = await DeviceOrientationEvent.requestPermission();
                return permission === 'granted';
            } catch (error) {
                console.error('设备方向权限请求失败:', error);
                return false;
            }
        }
        return true; // 其他平台默认允许
    }
    
    /**
     * 开始监听设备方向
     * @returns {Promise<boolean>}
     */
    async startListening() {
        if (!this.isSupported) {
            throw new Error('Device orientation is not supported');
        }
        
        if (this.isListening) {
            return true;
        }
        
        // 请求权限
        const hasPermission = await this.requestPermission();
        if (!hasPermission) {
            throw new Error('Device orientation permission denied');
        }
        
        // 绑定事件处理器
        this.handleDeviceOrientation = this.handleDeviceOrientation.bind(this);
        window.addEventListener('deviceorientation', this.handleDeviceOrientation);
        
        this.isListening = true;
        this.emit('started');
        
        return true;
    }
    
    /**
     * 停止监听设备方向
     */
    stopListening() {
        if (!this.isListening) return;
        
        window.removeEventListener('deviceorientation', this.handleDeviceOrientation);
        this.isListening = false;
        this.emit('stopped');
    }
    
    /**
     * 处理设备方向事件
     * @param {DeviceOrientationEvent} event - 设备方向事件
     */
    handleDeviceOrientation(event) {
        const newOrientation = {
            alpha: event.alpha || 0,
            beta: event.beta || 0,
            gamma: event.gamma || 0,
            absolute: event.absolute || false
        };
        
        // 数据平滑处理
        if (this.options.enableSmoothing) {
            this.smoothedOrientation = this.smoothOrientation(newOrientation);
        } else {
            this.smoothedOrientation = newOrientation;
        }
        
        // 检查变化阈值
        if (this.hasSignificantChange(this.smoothedOrientation, this.lastOrientation)) {
            this.currentOrientation = { ...this.smoothedOrientation };
            this.lastOrientation = { ...this.smoothedOrientation };
            
            // 触发事件
            this.emit('orientationchange', {
                orientation: this.currentOrientation,
                raw: newOrientation
            });
            
            // 检测特定方向
            this.detectOrientation();
        }
    }
    
    /**
     * 平滑方向数据
     * @param {Object} newOrientation - 新的方向数据
     * @returns {Object}
     */
    smoothOrientation(newOrientation) {
        const factor = this.options.smoothingFactor;
        
        return {
            alpha: this.smoothAngle(this.smoothedOrientation.alpha, newOrientation.alpha, factor),
            beta: this.smoothAngle(this.smoothedOrientation.beta, newOrientation.beta, factor),
            gamma: this.smoothAngle(this.smoothedOrientation.gamma, newOrientation.gamma, factor),
            absolute: newOrientation.absolute
        };
    }
    
    /**
     * 平滑角度值
     * @param {number} oldAngle - 旧角度
     * @param {number} newAngle - 新角度
     * @param {number} factor - 平滑因子
     * @returns {number}
     */
    smoothAngle(oldAngle, newAngle, factor) {
        // 处理角度跨越(0-360度)
        let diff = newAngle - oldAngle;
        if (diff > 180) diff -= 360;
        if (diff < -180) diff += 360;
        
        return oldAngle + diff * (1 - factor);
    }
    
    /**
     * 检查是否有显著变化
     * @param {Object} current - 当前方向
     * @param {Object} last - 上次方向
     * @returns {boolean}
     */
    hasSignificantChange(current, last) {
        const threshold = this.options.threshold;
        
        return Math.abs(current.alpha - last.alpha) > threshold ||
               Math.abs(current.beta - last.beta) > threshold ||
               Math.abs(current.gamma - last.gamma) > threshold;
    }
    
    /**
     * 检测设备方向
     */
    detectOrientation() {
        const { beta, gamma } = this.currentOrientation;
        let orientation = 'unknown';
        
        // 检测设备方向
        if (Math.abs(beta) < 45) {
            if (Math.abs(gamma) < 45) {
                orientation = 'flat';
            } else if (gamma > 45) {
                orientation = 'left';
            } else if (gamma < -45) {
                orientation = 'right';
            }
        } else if (beta > 45) {
            orientation = 'forward';
        } else if (beta < -45) {
            orientation = 'backward';
        }
        
        this.emit('orientationdetected', {
            orientation: orientation,
            angles: this.currentOrientation
        });
    }
    
    /**
     * 获取当前方向
     * @returns {Object}
     */
    getCurrentOrientation() {
        return { ...this.currentOrientation };
    }
    
    /**
     * 计算倾斜角度
     * @returns {Object}
     */
    getTiltAngles() {
        const { beta, gamma } = this.currentOrientation;
        
        return {
            pitch: beta,    // 前后倾斜
            roll: gamma,    // 左右倾斜
            magnitude: Math.sqrt(beta * beta + gamma * gamma)
        };
    }
    
    /**
     * 事件监听
     * @param {string} event - 事件名
     * @param {Function} callback - 回调函数
     */
    on(event, callback) {
        if (!this.listeners.has(event)) {
            this.listeners.set(event, []);
        }
        this.listeners.get(event).push(callback);
    }
    
    /**
     * 触发事件
     * @param {string} event - 事件名
     * @param {*} data - 事件数据
     */
    emit(event, data) {
        const callbacks = this.listeners.get(event) || [];
        callbacks.forEach(callback => callback(data));
    }
    
    /**
     * 销毁管理器
     */
    destroy() {
        this.stopListening();
        this.listeners.clear();
    }
}

// 使用示例
const orientationManager = new DeviceOrientationManager({
    enableSmoothing: true,
    smoothingFactor: 0.8,
    threshold: 2
});

// 检查支持
if (orientationManager.isDeviceOrientationSupported()) {
    // 开始监听
    try {
        await orientationManager.startListening();
        
        // 监听方向变化
        orientationManager.on('orientationchange', (data) => {
            console.log('方向变化:', data.orientation);
            updateUI(data.orientation);
        });
        
        // 监听方向检测
        orientationManager.on('orientationdetected', (data) => {
            console.log('设备方向:', data.orientation);
        });
        
    } catch (error) {
        console.error('启动设备方向监听失败:', error);
    }
} else {
    console.log('设备不支持方向感应');
}

17. 网络信息接口:网络状态感知

🔍 应用场景

网络状态监控、自适应加载、离线处理、性能优化

❌ 常见问题

// ❌ 简单的网络状态检查,信息有限
if (navigator.onLine) {
    console.log('在线');
} else {
    console.log('离线');
}

✅ 推荐方案

// ✅ 专业的网络信息管理器
class NetworkManager {
    constructor(options = {}) {
        this.options = {
            enableConnectionMonitoring: true,
            enableSpeedTest: true,
            speedTestInterval: 30000, // 30秒
            speedTestUrl: '/api/ping',
            ...options
        };
        
        this.listeners = new Map();
        this.connectionInfo = {
            online: navigator.onLine,
            type: 'unknown',
            effectiveType: 'unknown',
            downlink: 0,
            rtt: 0,
            saveData: false
        };
        
        this.speedTestResults = [];
        this.speedTestTimer = null;
        
        this.init();
    }
    
    /**
     * 初始化网络管理器
     */
    init() {
        // 监听在线/离线状态
        window.addEventListener('online', this.handleOnline.bind(this));
        window.addEventListener('offline', this.handleOffline.bind(this));
        
        // 监听网络连接变化
        if ('connection' in navigator) {
            const connection = navigator.connection;
            connection.addEventListener('change', this.handleConnectionChange.bind(this));
            this.updateConnectionInfo();
        }
        
        // 开始速度测试
        if (this.options.enableSpeedTest) {
            this.startSpeedTest();
        }
    }
    
    /**
     * 处理在线事件
     */
    handleOnline() {
        this.connectionInfo.online = true;
        this.emit('online', this.connectionInfo);
        this.emit('statuschange', this.connectionInfo);
        
        // 重新开始速度测试
        if (this.options.enableSpeedTest) {
            this.startSpeedTest();
        }
    }
    
    /**
     * 处理离线事件
     */
    handleOffline() {
        this.connectionInfo.online = false;
        this.emit('offline', this.connectionInfo);
        this.emit('statuschange', this.connectionInfo);
        
        // 停止速度测试
        this.stopSpeedTest();
    }
    
    /**
     * 处理连接变化
     */
    handleConnectionChange() {
        this.updateConnectionInfo();
        this.emit('connectionchange', this.connectionInfo);
        this.emit('statuschange', this.connectionInfo);
    }
    
    /**
     * 更新连接信息
     */
    updateConnectionInfo() {
        if ('connection' in navigator) {
            const connection = navigator.connection;
            
            this.connectionInfo = {
                ...this.connectionInfo,
                type: connection.type || 'unknown',
                effectiveType: connection.effectiveType || 'unknown',
                downlink: connection.downlink || 0,
                rtt: connection.rtt || 0,
                saveData: connection.saveData || false
            };
        }
    }
    
    /**
     * 开始网络速度测试
     */
    startSpeedTest() {
        if (this.speedTestTimer) {
            clearInterval(this.speedTestTimer);
        }
        
        // 立即执行一次
        this.performSpeedTest();
        
        // 定期执行
        this.speedTestTimer = setInterval(() => {
            this.performSpeedTest();
        }, this.options.speedTestInterval);
    }
    
    /**
     * 停止网络速度测试
     */
    stopSpeedTest() {
        if (this.speedTestTimer) {
            clearInterval(this.speedTestTimer);
            this.speedTestTimer = null;
        }
    }
    
    /**
     * 执行网络速度测试
     */
    async performSpeedTest() {
        if (!this.connectionInfo.online) return;
        
        try {
            const startTime = performance.now();
            const response = await fetch(this.options.speedTestUrl + '?t=' + Date.now(), {
                method: 'HEAD',
                cache: 'no-cache'
            });
            const endTime = performance.now();
            
            const latency = endTime - startTime;
            const result = {
                timestamp: Date.now(),
                latency: latency,
                success: response.ok
            };
            
            this.speedTestResults.push(result);
            
            // 保留最近10次结果
            if (this.speedTestResults.length > 10) {
                this.speedTestResults.shift();
            }
            
            this.emit('speedtest', result);
            
        } catch (error) {
            const result = {
                timestamp: Date.now(),
                latency: -1,
                success: false,
                error: error.message
            };
            
            this.speedTestResults.push(result);
            this.emit('speedtest', result);
        }
    }
    
    /**
     * 获取网络质量评估
     * @returns {Object}
     */
    getNetworkQuality() {
        const { effectiveType, downlink, rtt } = this.connectionInfo;
        let quality = 'unknown';
        let score = 0;
        
        // 基于有效连接类型评分
        switch (effectiveType) {
            case 'slow-2g':
                score += 1;
                break;
            case '2g':
                score += 2;
                break;
            case '3g':
                score += 3;
                break;
            case '4g':
                score += 4;
                break;
        }
        
        // 基于下行速度评分
        if (downlink > 10) score += 2;
        else if (downlink > 5) score += 1;
        
        // 基于RTT评分
        if (rtt < 100) score += 2;
        else if (rtt < 300) score += 1;
        
        // 基于速度测试结果评分
        const avgLatency = this.getAverageLatency();
        if (avgLatency > 0) {
            if (avgLatency < 100) score += 2;
            else if (avgLatency < 300) score += 1;
        }
        
        // 确定质量等级
        if (score >= 8) quality = 'excellent';
        else if (score >= 6) quality = 'good';
        else if (score >= 4) quality = 'fair';
        else if (score >= 2) quality = 'poor';
        else quality = 'very-poor';
        
        return {
            quality: quality,
            score: score,
            details: {
                effectiveType: effectiveType,
                downlink: downlink,
                rtt: rtt,
                avgLatency: avgLatency
            }
        };
    }
    
    /**
     * 获取平均延迟
     * @returns {number}
     */
    getAverageLatency() {
        const successfulTests = this.speedTestResults.filter(result => result.success);
        if (successfulTests.length === 0) return -1;
        
        const totalLatency = successfulTests.reduce((sum, result) => sum + result.latency, 0);
        return totalLatency / successfulTests.length;
    }
    
    /**
     * 获取连接信息
     * @returns {Object}
     */
    getConnectionInfo() {
        return { ...this.connectionInfo };
    }
    
    /**
     * 检查是否为慢速连接
     * @returns {boolean}
     */
    isSlowConnection() {
        const { effectiveType, saveData } = this.connectionInfo;
        return saveData || effectiveType === 'slow-2g' || effectiveType === '2g';
    }
    
    /**
     * 检查是否为移动网络
     * @returns {boolean}
     */
    isMobileConnection() {
        const { type } = this.connectionInfo;
        return type === 'cellular';
    }
    
    /**
     * 获取网络建议
     * @returns {Object}
     */
    getNetworkRecommendations() {
        const quality = this.getNetworkQuality();
        const recommendations = [];
        
        if (this.isSlowConnection()) {
            recommendations.push({
                type: 'performance',
                message: '检测到慢速网络,建议减少数据传输'
            });
        }
        
        if (this.isMobileConnection()) {
            recommendations.push({
                type: 'data-usage',
                message: '当前使用移动网络,注意流量消耗'
            });
        }
        
        if (quality.quality === 'poor' || quality.quality === 'very-poor') {
            recommendations.push({
                type: 'quality',
                message: '网络质量较差,建议启用离线模式'
            });
        }
        
        return {
            quality: quality,
            recommendations: recommendations
        };
    }
    
    /**
     * 事件监听
     * @param {string} event - 事件名
     * @param {Function} callback - 回调函数
     */
    on(event, callback) {
        if (!this.listeners.has(event)) {
            this.listeners.set(event, []);
        }
        this.listeners.get(event).push(callback);
    }
    
    /**
     * 触发事件
     * @param {string} event - 事件名
     * @param {*} data - 事件数据
     */
    emit(event, data) {
        const callbacks = this.listeners.get(event) || [];
        callbacks.forEach(callback => callback(data));
    }
    
    /**
     * 销毁网络管理器
     */
    destroy() {
        window.removeEventListener('online', this.handleOnline);
        window.removeEventListener('offline', this.handleOffline);
        
        if ('connection' in navigator) {
            navigator.connection.removeEventListener('change', this.handleConnectionChange);
        }
        
        this.stopSpeedTest();
        this.listeners.clear();
    }
}

// 使用示例
const networkManager = new NetworkManager({
    enableSpeedTest: true,
    speedTestInterval: 30000
});

// 监听网络状态变化
networkManager.on('statuschange', (info) => {
    console.log('网络状态:', info);
    updateNetworkIndicator(info);
});

networkManager.on('speedtest', (result) => {
    console.log('速度测试:', result);
});

// 获取网络建议
const recommendations = networkManager.getNetworkRecommendations();
console.log('网络建议:', recommendations);

18. 辅助功能接口:无障碍访问支持

🔍 应用场景

无障碍访问、屏幕阅读器支持、键盘导航、语音控制

❌ 常见问题

// ❌ 缺乏无障碍支持的组件
function createButton(text) {
    const button = document.createElement('button');
    button.textContent = text;
    return button;
}

✅ 推荐方案

// ✅ 专业的无障碍功能管理器
class AccessibilityManager {
    constructor(options = {}) {
        this.options = {
            enableKeyboardNavigation: true,
            enableScreenReader: true,
            enableHighContrast: false,
            enableFocusManagement: true,
            announceChanges: true,
            ...options
        };
        
        this.focusHistory = [];
        this.announcements = [];
        this.keyboardTrapStack = [];
        this.listeners = new Map();
        
        this.init();
    }
    
    /**
     * 初始化无障碍管理器
     */
    init() {
        // 创建屏幕阅读器公告区域
        this.createAnnouncementRegion();
        
        // 设置键盘导航
        if (this.options.enableKeyboardNavigation) {
            this.setupKeyboardNavigation();
        }
        
        // 设置焦点管理
        if (this.options.enableFocusManagement) {
            this.setupFocusManagement();
        }
        
        // 检测用户偏好
        this.detectUserPreferences();
    }
    
    /**
     * 创建屏幕阅读器公告区域
     */
    createAnnouncementRegion() {
        // 创建实时公告区域
        this.liveRegion = document.createElement('div');
        this.liveRegion.setAttribute('aria-live', 'polite');
        this.liveRegion.setAttribute('aria-atomic', 'true');
        this.liveRegion.className = 'sr-only';
        this.liveRegion.style.cssText = `
            position: absolute !important;
            width: 1px !important;
            height: 1px !important;
            padding: 0 !important;
            margin: -1px !important;
            overflow: hidden !important;
            clip: rect(0, 0, 0, 0) !important;
            white-space: nowrap !important;
            border: 0 !important;
        `;
        
        // 创建紧急公告区域
        this.assertiveRegion = document.createElement('div');
        this.assertiveRegion.setAttribute('aria-live', 'assertive');
        this.assertiveRegion.setAttribute('aria-atomic', 'true');
        this.assertiveRegion.className = 'sr-only';
        this.assertiveRegion.style.cssText = this.liveRegion.style.cssText;
        
        document.body.appendChild(this.liveRegion);
        document.body.appendChild(this.assertiveRegion);
    }
    
    /**
     * 设置键盘导航
     */
    setupKeyboardNavigation() {
        document.addEventListener('keydown', (event) => {
            this.handleKeyboardNavigation(event);
        });
        
        // 添加跳转链接
        this.addSkipLinks();
    }
    
    /**
     * 添加跳转链接
     */
    addSkipLinks() {
        const skipLinks = document.createElement('div');
        skipLinks.className = 'skip-links';
        skipLinks.innerHTML = `
            <a href="#main-content" class="skip-link">跳转到主要内容</a>
            <a href="#navigation" class="skip-link">跳转到导航</a>
        `;
        
        // 样式
        const style = document.createElement('style');
        style.textContent = `
            .skip-links {
                position: absolute;
                top: -40px;
                left: 6px;
                z-index: 1000;
            }
            .skip-link {
                position: absolute;
                top: -40px;
                left: 6px;
                background: #000;
                color: #fff;
                padding: 8px;
                text-decoration: none;
                z-index: 1000;
            }
            .skip-link:focus {
                top: 6px;
            }
        `;
        
        document.head.appendChild(style);
        document.body.insertBefore(skipLinks, document.body.firstChild);
    }
    
    /**
     * 处理键盘导航
     * @param {KeyboardEvent} event - 键盘事件
     */
    handleKeyboardNavigation(event) {
        const { key, ctrlKey, altKey, shiftKey } = event;
        
        // Escape键处理
        if (key === 'Escape') {
            this.handleEscape(event);
        }
        
        // Tab键陷阱处理
        if (key === 'Tab') {
            this.handleTabTrap(event);
        }
        
        // 方向键导航
        if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(key)) {
            this.handleArrowNavigation(event);
        }
        
        // 快捷键处理
        if (ctrlKey || altKey) {
            this.handleShortcuts(event);
        }
    }
    
    /**
     * 处理Escape键
     * @param {KeyboardEvent} event - 键盘事件
     */
    handleEscape(event) {
        // 关闭模态框
        const modal = document.querySelector('[role="dialog"]:not([aria-hidden="true"])');
        if (modal) {
            this.closeModal(modal);
            event.preventDefault();
            return;
        }
        
        // 退出焦点陷阱
        if (this.keyboardTrapStack.length > 0) {
            this.exitKeyboardTrap();
            event.preventDefault();
        }
    }
    
    /**
     * 处理Tab键陷阱
     * @param {KeyboardEvent} event - 键盘事件
     */
    handleTabTrap(event) {
        if (this.keyboardTrapStack.length === 0) return;
        
        const currentTrap = this.keyboardTrapStack[this.keyboardTrapStack.length - 1];
        const focusableElements = this.getFocusableElements(currentTrap.container);
        
        if (focusableElements.length === 0) return;
        
        const firstElement = focusableElements[0];
        const lastElement = focusableElements[focusableElements.length - 1];
        
        if (event.shiftKey) {
            // Shift + Tab
            if (document.activeElement === firstElement) {
                lastElement.focus();
                event.preventDefault();
            }
        } else {
            // Tab
            if (document.activeElement === lastElement) {
                firstElement.focus();
                event.preventDefault();
            }
        }
    }
    
    /**
     * 处理方向键导航
     * @param {KeyboardEvent} event - 键盘事件
     */
    handleArrowNavigation(event) {
        const activeElement = document.activeElement;
        const role = activeElement.getAttribute('role');
        
        // 处理菜单导航
        if (role === 'menuitem' || activeElement.closest('[role="menu"]')) {
            this.handleMenuNavigation(event);
        }
        
        // 处理表格导航
        if (activeElement.tagName === 'TD' || activeElement.tagName === 'TH') {
            this.handleTableNavigation(event);
        }
        
        // 处理网格导航
        if (role === 'gridcell' || activeElement.closest('[role="grid"]')) {
            this.handleGridNavigation(event);
        }
    }
    
    /**
     * 设置焦点管理
     */
    setupFocusManagement() {
        // 监听焦点变化
        document.addEventListener('focusin', (event) => {
            this.handleFocusIn(event);
        });
        
        document.addEventListener('focusout', (event) => {
            this.handleFocusOut(event);
        });
        
        // 监听DOM变化
        const observer = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                if (mutation.type === 'childList') {
                    this.handleDOMChanges(mutation);
                }
            });
        });
        
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    }
    
    /**
     * 处理焦点进入
     * @param {FocusEvent} event - 焦点事件
     */
    handleFocusIn(event) {
        const element = event.target;
        
        // 记录焦点历史
        this.focusHistory.push({
            element: element,
            timestamp: Date.now()
        });
        
        // 限制历史记录长度
        if (this.focusHistory.length > 10) {
            this.focusHistory.shift();
        }
        
        // 触发焦点事件
        this.emit('focuschange', {
            element: element,
            type: 'focusin'
        });
    }
    
    /**
     * 处理焦点离开
     * @param {FocusEvent} event - 焦点事件
     */
    handleFocusOut(event) {
        this.emit('focuschange', {
            element: event.target,
            type: 'focusout'
        });
    }
    
    /**
     * 检测用户偏好
     */
    detectUserPreferences() {
        // 检测减少动画偏好
        if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
            this.setReducedMotion(true);
        }
        
        // 检测高对比度偏好
        if (window.matchMedia('(prefers-contrast: high)').matches) {
            this.setHighContrast(true);
        }
        
        // 检测颜色方案偏好
        if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
            this.setDarkMode(true);
        }
    }
    
    /**
     * 公告消息给屏幕阅读器
     * @param {string} message - 消息内容
     * @param {string} priority - 优先级 ('polite' | 'assertive')
     */
    announce(message, priority = 'polite') {
        const region = priority === 'assertive' ? this.assertiveRegion : this.liveRegion;
        
        // 清空区域
        region.textContent = '';
        
        // 延迟添加消息,确保屏幕阅读器能检测到变化
        setTimeout(() => {
            region.textContent = message;
        }, 100);
        
        // 记录公告
        this.announcements.push({
            message: message,
            priority: priority,
            timestamp: Date.now()
        });
        
        if (this.options.announceChanges) {
            this.emit('announcement', {
                message: message,
                priority: priority
            });
        }
    }
    
    /**
     * 创建键盘陷阱
     * @param {HTMLElement} container - 容器元素
     * @param {Object} options - 选项
     */
    createKeyboardTrap(container, options = {}) {
        const trap = {
            container: container,
            previousFocus: document.activeElement,
            options: {
                returnFocus: true,
                ...options
            }
        };
        
        this.keyboardTrapStack.push(trap);
        
        // 设置初始焦点
        const focusableElements = this.getFocusableElements(container);
        if (focusableElements.length > 0) {
            focusableElements[0].focus();
        }
        
        return trap;
    }
    
    /**
     * 退出键盘陷阱
     */
    exitKeyboardTrap() {
        if (this.keyboardTrapStack.length === 0) return;
        
        const trap = this.keyboardTrapStack.pop();
        
        // 恢复焦点
        if (trap.options.returnFocus && trap.previousFocus) {
            trap.previousFocus.focus();
        }
    }
    
    /**
     * 获取可聚焦元素
     * @param {HTMLElement} container - 容器元素
     * @returns {HTMLElement[]}
     */
    getFocusableElements(container) {
        const selector = [
            'a[href]',
            'button:not([disabled])',
            'input:not([disabled])',
            'select:not([disabled])',
            'textarea:not([disabled])',
            '[tabindex]:not([tabindex="-1"])',
            '[contenteditable="true"]'
        ].join(', ');
        
        return Array.from(container.querySelectorAll(selector))
            .filter(element => {
                return element.offsetWidth > 0 && 
                       element.offsetHeight > 0 && 
                       !element.hasAttribute('aria-hidden');
            });
    }
    
    /**
     * 设置减少动画
     * @param {boolean} enabled - 是否启用
     */
    setReducedMotion(enabled) {
        document.documentElement.classList.toggle('reduce-motion', enabled);
        this.emit('preferencechange', {
            type: 'reduced-motion',
            enabled: enabled
        });
    }
    
    /**
     * 设置高对比度
     * @param {boolean} enabled - 是否启用
     */
    setHighContrast(enabled) {
        document.documentElement.classList.toggle('high-contrast', enabled);
        this.emit('preferencechange', {
            type: 'high-contrast',
            enabled: enabled
        });
    }
    
    /**
     * 设置深色模式
     * @param {boolean} enabled - 是否启用
     */
    setDarkMode(enabled) {
        document.documentElement.classList.toggle('dark-mode', enabled);
        this.emit('preferencechange', {
            type: 'dark-mode',
            enabled: enabled
        });
    }
    
    /**
     * 创建无障碍按钮
     * @param {Object} config - 按钮配置
     * @returns {HTMLElement}
     */
    createAccessibleButton(config) {
        const button = document.createElement('button');
        
        // 基本属性
        button.textContent = config.text;
        button.type = config.type || 'button';
        
        // 无障碍属性
        if (config.ariaLabel) {
            button.setAttribute('aria-label', config.ariaLabel);
        }
        
        if (config.ariaDescribedBy) {
            button.setAttribute('aria-describedby', config.ariaDescribedBy);
        }
        
        if (config.ariaExpanded !== undefined) {
            button.setAttribute('aria-expanded', config.ariaExpanded);
        }
        
        if (config.ariaControls) {
            button.setAttribute('aria-controls', config.ariaControls);
        }
        
        // 事件处理
        if (config.onClick) {
            button.addEventListener('click', config.onClick);
        }
        
        // 键盘事件
        button.addEventListener('keydown', (event) => {
            if (event.key === 'Enter' || event.key === ' ') {
                event.preventDefault();
                button.click();
            }
        });
        
        return button;
    }
    
    /**
     * 事件监听
     * @param {string} event - 事件名
     * @param {Function} callback - 回调函数
     */
    on(event, callback) {
        if (!this.listeners.has(event)) {
            this.listeners.set(event, []);
        }
        this.listeners.get(event).push(callback);
    }
    
    /**
     * 触发事件
     * @param {string} event - 事件名
     * @param {*} data - 事件数据
     */
    emit(event, data) {
        const callbacks = this.listeners.get(event) || [];
        callbacks.forEach(callback => callback(data));
    }
    
    /**
     * 销毁无障碍管理器
     */
    destroy() {
        // 清理DOM元素
        if (this.liveRegion) {
            this.liveRegion.remove();
        }
        if (this.assertiveRegion) {
            this.assertiveRegion.remove();
        }
        
        // 清理事件监听器
        this.listeners.clear();
        
        // 清理键盘陷阱
        while (this.keyboardTrapStack.length > 0) {
            this.exitKeyboardTrap();
        }
    }
}

// 使用示例
const accessibilityManager = new AccessibilityManager({
    enableKeyboardNavigation: true,
    enableScreenReader: true,
    announceChanges: true
});

// 创建无障碍按钮
const button = accessibilityManager.createAccessibleButton({
    text: '打开菜单',
    ariaLabel: '打开主导航菜单',
    ariaExpanded: false,
    ariaControls: 'main-menu',
    onClick: () => {
        // 切换菜单状态
        const isExpanded = button.getAttribute('aria-expanded') === 'true';
        button.setAttribute('aria-expanded', !isExpanded);
        
        // 公告状态变化
        accessibilityManager.announce(
            isExpanded ? '菜单已关闭' : '菜单已打开'
        );
    }
});

// 监听无障碍事件
accessibilityManager.on('announcement', (data) => {
    console.log('屏幕阅读器公告:', data.message);
});

accessibilityManager.on('focuschange', (data) => {
    console.log('焦点变化:', data.element.tagName);
});

🎯 总结与展望

核心要点回顾

通过本篇文章,我们深入探讨了HTML DOM API的高级接口:

  1. Web Worker接口 - 实现多线程处理,提升应用性能
  2. WebRTC接口 - 构建实时通信应用
  3. WebGL接口 - 创建高性能3D图形
  4. 触摸手势接口 - 优化移动端交互体验
  5. 地理位置接口 - 实现位置感知功能
  6. 设备方向接口 - 感知设备状态变化
  7. 网络信息接口 - 智能网络状态管理
  8. 辅助功能接口 - 构建无障碍访问体验

实践建议

  1. 渐进增强 - 始终提供基础功能的降级方案
  2. 性能优化 - 合理使用高级API,避免过度消耗资源
  3. 用户体验 - 关注不同设备和网络环境下的体验
  4. 无障碍访问 - 确保所有用户都能正常使用应用

相关资源


感谢阅读! 如果这篇文章对你有帮助,请点赞、收藏并分享给更多的开发者。让我们一起构建更好的Web应用!

💡 提示:本文涵盖的API接口较多,建议结合实际项目需求选择性学习和应用。记住,技术的价值在于解决实际问题,而不是为了使用而使用。

🏗️ JavaScript类深度解析 - 从构造函数到现代特性的完整指南

🎯 学习目标:深入理解JavaScript类的核心特性,掌握从基础构造函数到最新私有字段的完整知识体系

📊 难度等级:中级-高级
🏷️ 技术标签#JavaScript #ES6+ #类 #面向对象 #现代特性
⏱️ 阅读时间:约12分钟


🌟 引言

在现代JavaScript开发中,你是否遇到过这样的困扰:

  • 类语法混乱:constructor、extends、super这些关键字用法总是搞不清楚?
  • 私有属性困惑:想要实现真正的私有属性,但不知道最新的语法规范?
  • 静态方法迷茫:static关键字的使用场景和最佳实践不够清晰?
  • 现代特性陌生:公有类字段、静态初始化块等新特性不知道如何应用?

今天深度解析JavaScript类的6个核心特性,从传统构造函数到最新的现代语法,让你的面向对象编程更加专业和高效!


💡 核心特性详解

1. 构造函数(Constructor):类的初始化核心

🔍 应用场景

构造函数是类实例化时自动调用的特殊方法,用于初始化对象的属性和状态。

❌ 常见问题

很多开发者在构造函数中犯这些错误:

// ❌ 错误示例:构造函数中的常见问题
class User {
  constructor(name, email) {
    // 问题1:没有参数验证
    this.name = name;
    this.email = email;
    
    // 问题2:在构造函数中执行异步操作
    this.loadUserData();
    
    // 问题3:没有设置默认值
    this.isActive = true;
  }
  
  async loadUserData() {
    // 异步操作不应该在构造函数中执行
    const data = await fetch('/api/user');
    this.userData = await data.json();
  }
}

✅ 推荐方案

正确的构造函数应该同步、简洁、安全:

/**
 * 用户类的正确构造函数实现
 * @description 提供参数验证、默认值设置和类型检查
 */
class User {
  constructor(name, email, options = {}) {
    // ✅ 参数验证
    if (!name || typeof name !== 'string') {
      throw new Error('Name must be a non-empty string');
    }
    
    if (!this.isValidEmail(email)) {
      throw new Error('Invalid email format');
    }
    
    // ✅ 属性初始化
    this.name = name.trim();
    this.email = email.toLowerCase();
    this.isActive = options.isActive ?? true;
    this.role = options.role || 'user';
    this.createdAt = new Date();
    
    // ✅ 调用初始化方法
    this.initializeDefaults();
  }
  
  /**
   * 邮箱格式验证
   * @param {string} email - 邮箱地址
   * @returns {boolean} 是否有效
   */
  isValidEmail = (email) => {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return emailRegex.test(email);
  };
  
  /**
   * 初始化默认设置
   * @description 设置用户的默认配置和权限
   */
  initializeDefaults = () => {
    this.permissions = this.role === 'admin' ? ['read', 'write', 'delete'] : ['read'];
    this.settings = {
      theme: 'light',
      notifications: true,
      language: 'zh-CN'
    };
  };
}

💡 核心要点

  • 同步执行:构造函数必须是同步的,不能包含异步操作
  • 参数验证:始终验证传入的参数,提供清晰的错误信息
  • 默认值处理:使用ES6默认参数和空值合并操作符
  • 职责单一:只负责初始化,复杂逻辑应该分离到其他方法

🎯 实际应用

在实际项目中的用户管理系统应用:

// 实际项目中的应用
class UserManager {
  constructor(apiConfig) {
    this.apiUrl = apiConfig.baseUrl;
    this.timeout = apiConfig.timeout || 5000;
    this.users = new Map();
  }
  
  /**
   * 创建新用户(异步操作分离)
   * @param {Object} userData - 用户数据
   * @returns {Promise<User>} 创建的用户实例
   */
  async createUser(userData) {
    // 先创建用户实例
    const user = new User(userData.name, userData.email, userData.options);
    
    // 再执行异步操作
    try {
      const response = await this.saveToDatabase(user);
      user.id = response.id;
      this.users.set(user.id, user);
      return user;
    } catch (error) {
      throw new Error(`Failed to create user: ${error.message}`);
    }
  }
}

2. 继承(extends):类的扩展机制

🔍 应用场景

使用extends关键字创建类的继承关系,实现代码复用和多态性。

❌ 常见问题

继承中经常出现的错误模式:

// ❌ 错误示例:继承中的常见问题
class Animal {
  constructor(name) {
    this.name = name;
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    // 问题1:忘记调用super()
    this.breed = breed; // ReferenceError: Must call super constructor
    
    // 问题2:super()调用位置错误
    this.age = 0;
    super(name); // 应该在最前面调用
  }
  
  // 问题3:方法重写时没有考虑父类逻辑
  speak() {
    return "Woof!";
  }
}

✅ 推荐方案

正确的继承实现应该遵循最佳实践:

/**
 * 动物基类
 * @description 定义所有动物的通用属性和方法
 */
class Animal {
  constructor(name, species) {
    this.name = name;
    this.species = species;
    this.isAlive = true;
    this.energy = 100;
  }
  
  /**
   * 动物发声方法(抽象方法)
   * @returns {string} 动物的叫声
   */
  speak() {
    throw new Error('speak() method must be implemented by subclass');
  }
  
  /**
   * 移动方法
   * @param {number} distance - 移动距离
   */
  move(distance) {
    this.energy -= distance * 0.1;
    console.log(`${this.name} moved ${distance} meters`);
  }
  
  /**
   * 休息方法
   * @param {number} hours - 休息时间
   */
  rest(hours) {
    this.energy = Math.min(100, this.energy + hours * 10);
    console.log(`${this.name} rested for ${hours} hours`);
  }
}

/**
 * 狗类 - 继承自Animal
 * @description 实现狗特有的行为和属性
 */
class Dog extends Animal {
  constructor(name, breed, owner) {
    // ✅ 首先调用父类构造函数
    super(name, 'Canine');
    
    // ✅ 然后初始化子类特有属性
    this.breed = breed;
    this.owner = owner;
    this.loyalty = 100;
    this.tricks = [];
  }
  
  /**
   * 实现父类的抽象方法
   * @returns {string} 狗的叫声
   */
  speak() {
    return `${this.name} says: Woof! Woof!`;
  }
  
  /**
   * 狗特有的方法 - 学习技巧
   * @param {string} trick - 要学习的技巧
   */
  learnTrick(trick) {
    if (!this.tricks.includes(trick)) {
      this.tricks.push(trick);
      console.log(`${this.name} learned a new trick: ${trick}`);
    }
  }
  
  /**
   * 重写父类方法,添加狗特有的行为
   * @param {number} distance - 移动距离
   */
  move(distance) {
    // ✅ 调用父类方法
    super.move(distance);
    
    // ✅ 添加子类特有逻辑
    if (distance > 100) {
      this.loyalty += 5; // 长距离移动增加忠诚度
      console.log(`${this.name}'s loyalty increased!`);
    }
  }
}

/**
 * 工作犬类 - 多层继承
 * @description 具有特殊工作能力的狗
 */
class WorkingDog extends Dog {
  constructor(name, breed, owner, jobType) {
    super(name, breed, owner);
    this.jobType = jobType;
    this.workExperience = 0;
    this.isOnDuty = false;
  }
  
  /**
   * 开始工作
   * @param {number} hours - 工作时间
   */
  startWork(hours) {
    if (this.energy < 30) {
      console.log(`${this.name} is too tired to work`);
      return;
    }
    
    this.isOnDuty = true;
    this.workExperience += hours;
    this.energy -= hours * 5;
    console.log(`${this.name} worked as ${this.jobType} for ${hours} hours`);
  }
  
  /**
   * 重写speak方法,工作犬有不同的叫声
   * @returns {string} 工作犬的叫声
   */
  speak() {
    const baseSound = super.speak();
    return this.isOnDuty ? 
      `${baseSound} (Alert and focused)` : 
      baseSound;
  }
}

💡 核心要点

  • super()调用:子类构造函数必须首先调用super()
  • 方法重写:可以完全重写或扩展父类方法
  • 多层继承:支持多层继承链,但要注意复杂度
  • 抽象方法:父类可以定义需要子类实现的抽象方法

🎯 实际应用

在UI组件库中的继承应用:

// 实际项目中的组件继承
class BaseComponent {
  constructor(element, options = {}) {
    this.element = element;
    this.options = { ...this.defaultOptions, ...options };
    this.isInitialized = false;
    this.eventListeners = new Map();
  }
  
  get defaultOptions() {
    return {
      className: 'base-component',
      disabled: false
    };
  }
  
  init() {
    this.render();
    this.bindEvents();
    this.isInitialized = true;
  }
  
  render() {
    throw new Error('render() must be implemented by subclass');
  }
  
  destroy() {
    this.eventListeners.forEach((listener, event) => {
      this.element.removeEventListener(event, listener);
    });
    this.eventListeners.clear();
  }
}

class Button extends BaseComponent {
  get defaultOptions() {
    return {
      ...super.defaultOptions,
      className: 'btn',
      type: 'button',
      text: 'Click me'
    };
  }
  
  render() {
    this.element.className = this.options.className;
    this.element.textContent = this.options.text;
    this.element.type = this.options.type;
    this.element.disabled = this.options.disabled;
  }
  
  bindEvents() {
    const clickHandler = (e) => this.handleClick(e);
    this.element.addEventListener('click', clickHandler);
    this.eventListeners.set('click', clickHandler);
  }
  
  handleClick(event) {
    if (this.options.disabled) return;
    
    this.element.classList.add('btn-clicked');
    setTimeout(() => {
      this.element.classList.remove('btn-clicked');
    }, 150);
    
    // 触发自定义事件
    this.element.dispatchEvent(new CustomEvent('button:click', {
      detail: { originalEvent: event }
    }));
  }
}

3. 私有元素(Private Fields):真正的封装

🔍 应用场景

使用#语法创建真正私有的属性和方法,实现数据封装和信息隐藏。

❌ 常见问题

传统的"私有"属性实现方式存在问题:

// ❌ 错误示例:伪私有属性
class BankAccount {
  constructor(accountNumber, initialBalance) {
    // 问题1:下划线约定不是真正的私有
    this._accountNumber = accountNumber;
    this._balance = initialBalance;
    
    // 问题2:Symbol虽然更安全,但仍可访问
    this[Symbol.for('secret')] = 'sensitive data';
  }
  
  // 问题3:WeakMap方式过于复杂
  getBalance() {
    return this._balance; // 外部仍可直接访问 instance._balance
  }
}

// 外部可以直接访问"私有"属性
const account = new BankAccount('123456', 1000);
console.log(account._balance); // 1000 - 不应该被访问
account._balance = 999999; // 可以被恶意修改

✅ 推荐方案

使用现代JavaScript的真正私有字段:

/**
 * 银行账户类 - 使用真正的私有字段
 * @description 实现安全的账户管理,保护敏感数据
 */
class BankAccount {
  // ✅ 私有字段声明
  #accountNumber;
  #balance;
  #pin;
  #transactionHistory;
  #isLocked;
  
  // ✅ 私有静态字段
  static #bankCode = 'BANK001';
  static #encryptionKey = 'secret-key-2023';
  
  constructor(accountNumber, initialBalance, pin) {
    // 参数验证
    if (!accountNumber || initialBalance < 0 || !pin) {
      throw new Error('Invalid account parameters');
    }
    
    // ✅ 初始化私有字段
    this.#accountNumber = accountNumber;
    this.#balance = initialBalance;
    this.#pin = this.#hashPin(pin);
    this.#transactionHistory = [];
    this.#isLocked = false;
    
    // 公有属性
    this.accountType = 'savings';
    this.createdAt = new Date();
  }
  
  /**
   * 私有方法 - PIN哈希处理
   * @param {string} pin - 原始PIN
   * @returns {string} 哈希后的PIN
   */
  #hashPin(pin) {
    // 简化的哈希实现(实际项目中应使用专业的加密库)
    return btoa(pin + BankAccount.#encryptionKey);
  }
  
  /**
   * 私有方法 - 验证PIN
   * @param {string} inputPin - 输入的PIN
   * @returns {boolean} 验证结果
   */
  #verifyPin(inputPin) {
    return this.#hashPin(inputPin) === this.#pin;
  }
  
  /**
   * 私有方法 - 记录交易
   * @param {string} type - 交易类型
   * @param {number} amount - 交易金额
   * @param {string} description - 交易描述
   */
  #recordTransaction(type, amount, description) {
    this.#transactionHistory.push({
      id: Date.now(),
      type,
      amount,
      description,
      timestamp: new Date(),
      balance: this.#balance
    });
    
    // 只保留最近100条记录
    if (this.#transactionHistory.length > 100) {
      this.#transactionHistory = this.#transactionHistory.slice(-100);
    }
  }
  
  /**
   * 获取账户余额
   * @param {string} pin - PIN码
   * @returns {number} 账户余额
   */
  getBalance(pin) {
    if (this.#isLocked) {
      throw new Error('Account is locked');
    }
    
    if (!this.#verifyPin(pin)) {
      this.#lockAccount();
      throw new Error('Invalid PIN');
    }
    
    return this.#balance;
  }
  
  /**
   * 存款操作
   * @param {number} amount - 存款金额
   * @param {string} pin - PIN码
   * @param {string} description - 交易描述
   */
  deposit(amount, pin, description = 'Deposit') {
    if (this.#isLocked || !this.#verifyPin(pin) || amount <= 0) {
      throw new Error('Invalid deposit operation');
    }
    
    this.#balance += amount;
    this.#recordTransaction('deposit', amount, description);
    
    console.log(`Deposited $${amount}. New balance: $${this.#balance}`);
  }
  
  /**
   * 取款操作
   * @param {number} amount - 取款金额
   * @param {string} pin - PIN码
   * @param {string} description - 交易描述
   */
  withdraw(amount, pin, description = 'Withdrawal') {
    if (this.#isLocked || !this.#verifyPin(pin)) {
      throw new Error('Invalid withdrawal operation');
    }
    
    if (amount > this.#balance) {
      throw new Error('Insufficient funds');
    }
    
    this.#balance -= amount;
    this.#recordTransaction('withdrawal', amount, description);
    
    console.log(`Withdrew $${amount}. New balance: $${this.#balance}`);
  }
  
  /**
   * 私有方法 - 锁定账户
   */
  #lockAccount() {
    this.#isLocked = true;
    console.log('Account has been locked due to security concerns');
  }
  
  /**
   * 获取交易历史(只返回安全信息)
   * @param {string} pin - PIN码
   * @returns {Array} 交易历史
   */
  getTransactionHistory(pin) {
    if (!this.#verifyPin(pin)) {
      throw new Error('Invalid PIN');
    }
    
    // 返回脱敏的交易历史
    return this.#transactionHistory.map(transaction => ({
      id: transaction.id,
      type: transaction.type,
      amount: transaction.amount,
      description: transaction.description,
      timestamp: transaction.timestamp
      // 注意:不返回余额信息
    }));
  }
  
  /**
   * 获取账户摘要信息
   * @returns {Object} 公开的账户信息
   */
  getAccountSummary() {
    return {
      accountType: this.accountType,
      createdAt: this.createdAt,
      isLocked: this.#isLocked,
      // 账户号码脱敏显示
      accountNumber: this.#accountNumber.replace(/\d(?=\d{4})/g, '*')
    };
  }
}

💡 核心要点

  • 真正私有:#字段无法从类外部访问,提供真正的封装
  • 编译时检查:访问不存在的私有字段会在编译时报错
  • 继承限制:私有字段不能被子类访问,确保封装性
  • 性能优化:私有字段访问比WeakMap方式更高效

🎯 实际应用

在状态管理库中的私有字段应用:

// 实际项目中的状态管理应用
class StateManager {
  // 私有状态存储
  #state;
  #subscribers;
  #middleware;
  #isDispatching;
  
  constructor(initialState = {}) {
    this.#state = { ...initialState };
    this.#subscribers = new Set();
    this.#middleware = [];
    this.#isDispatching = false;
  }
  
  /**
   * 私有方法 - 通知订阅者
   */
  #notifySubscribers() {
    this.#subscribers.forEach(callback => {
      try {
        callback(this.#state);
      } catch (error) {
        console.error('Subscriber error:', error);
      }
    });
  }
  
  /**
   * 私有方法 - 应用中间件
   */
  #applyMiddleware(action) {
    return this.#middleware.reduce((acc, middleware) => {
      return middleware(acc);
    }, action);
  }
  
  /**
   * 获取当前状态(只读)
   * @returns {Object} 状态的深拷贝
   */
  getState() {
    return JSON.parse(JSON.stringify(this.#state));
  }
  
  /**
   * 分发动作
   * @param {Object} action - 要分发的动作
   */
  dispatch(action) {
    if (this.#isDispatching) {
      throw new Error('Cannot dispatch while dispatching');
    }
    
    try {
      this.#isDispatching = true;
      const processedAction = this.#applyMiddleware(action);
      
      // 应用状态变更
      this.#state = this.#reducer(this.#state, processedAction);
      
      // 通知订阅者
      this.#notifySubscribers();
    } finally {
      this.#isDispatching = false;
    }
  }
  
  /**
   * 私有reducer方法
   * @param {Object} state - 当前状态
   * @param {Object} action - 动作
   * @returns {Object} 新状态
   */
  #reducer(state, action) {
    // 简化的reducer实现
    switch (action.type) {
      case 'SET_VALUE':
        return { ...state, [action.key]: action.value };
      case 'RESET':
        return {};
      default:
        return state;
    }
  }
  
  /**
   * 订阅状态变化
   * @param {Function} callback - 回调函数
   * @returns {Function} 取消订阅函数
   */
  subscribe(callback) {
    this.#subscribers.add(callback);
    
    // 返回取消订阅函数
    return () => {
      this.#subscribers.delete(callback);
    };
  }
}

4. 公有类字段(Public Class Fields):简化的属性声明

🔍 应用场景

直接在类体中声明公有属性,无需在构造函数中初始化,提供更清晰的类结构。

❌ 常见问题

传统的属性声明方式存在一些不便:

// ❌ 传统方式:所有属性都在构造函数中声明
class GamePlayer {
  constructor(name) {
    // 问题1:属性声明分散,不易查看类结构
    this.name = name;
    this.level = 1;
    this.health = 100;
    this.mana = 50;
    this.inventory = [];
    this.skills = new Map();
    this.isOnline = false;
    this.lastLoginTime = null;
    
    // 问题2:默认值和初始化逻辑混在一起
    this.stats = {
      strength: 10,
      agility: 10,
      intelligence: 10
    };
    
    // 问题3:方法绑定需要手动处理
    this.handleLevelUp = this.handleLevelUp.bind(this);
  }
  
  handleLevelUp() {
    this.level++;
  }
}

✅ 推荐方案

使用公有类字段简化属性声明:

/**
 * 游戏玩家类 - 使用公有类字段
 * @description 展示现代JavaScript类字段的最佳实践
 */
class GamePlayer {
  // ✅ 公有类字段声明 - 基础属性
  level = 1;
  health = 100;
  maxHealth = 100;
  mana = 50;
  maxMana = 50;
  experience = 0;
  
  // ✅ 复杂类型的默认值
  inventory = [];
  skills = new Map();
  achievements = new Set();
  
  // ✅ 对象类型的默认值
  stats = {
    strength: 10,
    agility: 10,
    intelligence: 10,
    vitality: 10
  };
  
  // ✅ 状态相关字段
  isOnline = false;
  isInCombat = false;
  lastLoginTime = null;
  currentLocation = 'starting_town';
  
  // ✅ 配置相关字段
  settings = {
    soundEnabled: true,
    musicVolume: 0.8,
    effectsVolume: 0.6,
    autoSave: true
  };
  
  // ✅ 箭头函数方法自动绑定this
  handleLevelUp = () => {
    const oldLevel = this.level;
    this.level++;
    this.experience = 0;
    
    // 升级时提升属性
    this.maxHealth += 10;
    this.maxMana += 5;
    this.health = this.maxHealth; // 升级时恢复满血
    this.mana = this.maxMana;
    
    // 每5级获得属性点
    if (this.level % 5 === 0) {
      this.stats.strength += 2;
      this.stats.agility += 2;
      this.stats.intelligence += 2;
      this.stats.vitality += 2;
    }
    
    console.log(`Level up! ${oldLevel}${this.level}`);
    this.onLevelUp?.(this.level, oldLevel);
  };
  
  // ✅ 经验值计算方法
  gainExperience = (amount) => {
    this.experience += amount;
    const requiredExp = this.getRequiredExperience();
    
    if (this.experience >= requiredExp) {
      this.handleLevelUp();
    }
    
    console.log(`Gained ${amount} XP. Progress: ${this.experience}/${requiredExp}`);
  };
  
  // ✅ 技能学习方法
  learnSkill = (skillName, skillData) => {
    if (this.level < skillData.requiredLevel) {
      throw new Error(`Level ${skillData.requiredLevel} required to learn ${skillName}`);
    }
    
    this.skills.set(skillName, {
      ...skillData,
      learnedAt: new Date(),
      level: 1,
      experience: 0
    });
    
    console.log(`Learned new skill: ${skillName}`);
  };
  
  constructor(name, playerClass = 'warrior') {
    // ✅ 构造函数只处理必需的参数
    if (!name || typeof name !== 'string') {
      throw new Error('Player name is required');
    }
    
    this.name = name.trim();
    this.playerClass = playerClass;
    this.id = this.generatePlayerId();
    this.createdAt = new Date();
    
    // ✅ 根据职业调整初始属性
    this.applyClassModifiers(playerClass);
    
    // ✅ 初始化基础技能
    this.initializeClassSkills(playerClass);
  }
  
  /**
   * 生成唯一玩家ID
   * @returns {string} 玩家ID
   */
  generatePlayerId() {
    return `player_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }
  
  /**
   * 应用职业修正
   * @param {string} playerClass - 玩家职业
   */
  applyClassModifiers(playerClass) {
    const classModifiers = {
      warrior: { strength: +5, vitality: +3, health: +20 },
      mage: { intelligence: +5, agility: +2, mana: +20 },
      rogue: { agility: +5, strength: +2, health: +10 },
      cleric: { intelligence: +3, vitality: +3, mana: +15, health: +15 }
    };
    
    const modifiers = classModifiers[playerClass] || {};
    
    // 应用属性修正
    Object.entries(modifiers).forEach(([stat, bonus]) => {
      if (stat === 'health') {
        this.maxHealth += bonus;
        this.health = this.maxHealth;
      } else if (stat === 'mana') {
        this.maxMana += bonus;
        this.mana = this.maxMana;
      } else if (this.stats[stat] !== undefined) {
        this.stats[stat] += bonus;
      }
    });
  }
  
  /**
   * 初始化职业技能
   * @param {string} playerClass - 玩家职业
   */
  initializeClassSkills(playerClass) {
    const classSkills = {
      warrior: [
        { name: 'Slash', damage: 15, manaCost: 0, requiredLevel: 1 },
        { name: 'Shield Block', defense: 10, manaCost: 5, requiredLevel: 1 }
      ],
      mage: [
        { name: 'Fireball', damage: 20, manaCost: 10, requiredLevel: 1 },
        { name: 'Heal', healing: 15, manaCost: 8, requiredLevel: 1 }
      ],
      rogue: [
        { name: 'Backstab', damage: 18, manaCost: 5, requiredLevel: 1 },
        { name: 'Stealth', duration: 10, manaCost: 15, requiredLevel: 1 }
      ],
      cleric: [
        { name: 'Heal', healing: 25, manaCost: 10, requiredLevel: 1 },
        { name: 'Bless', buff: 'protection', manaCost: 12, requiredLevel: 1 }
      ]
    };
    
    const skills = classSkills[playerClass] || [];
    skills.forEach(skill => {
      this.skills.set(skill.name, { ...skill, level: 1, experience: 0 });
    });
  }
  
  /**
   * 计算升级所需经验
   * @returns {number} 所需经验值
   */
  getRequiredExperience() {
    return Math.floor(100 * Math.pow(1.5, this.level - 1));
  }
  
  /**
   * 获取玩家状态摘要
   * @returns {Object} 玩家状态
   */
  getStatus() {
    return {
      name: this.name,
      class: this.playerClass,
      level: this.level,
      health: `${this.health}/${this.maxHealth}`,
      mana: `${this.mana}/${this.maxMana}`,
      experience: `${this.experience}/${this.getRequiredExperience()}`,
      location: this.currentLocation,
      skillCount: this.skills.size,
      achievementCount: this.achievements.size
    };
  }
}

💡 核心要点

  • 清晰结构:类字段声明让类的结构一目了然
  • 自动绑定:箭头函数方法自动绑定this上下文
  • 默认值:可以直接为字段设置默认值
  • 类型明确:字段声明使得属性类型更加明确

🎯 实际应用

在React组件中的类字段应用:

// 实际项目中的React组件应用
class TodoComponent extends React.Component {
  // ✅ 状态字段声明
  state = {
    todos: [],
    filter: 'all',
    isLoading: false,
    error: null
  };
  
  // ✅ 配置字段
  maxTodos = 100;
  autoSaveInterval = 5000;
  
  // ✅ 引用字段
  inputRef = React.createRef();
  containerRef = React.createRef();
  
  // ✅ 方法自动绑定
  handleAddTodo = (text) => {
    if (this.state.todos.length >= this.maxTodos) {
      this.setState({ error: 'Maximum todos reached' });
      return;
    }
    
    const newTodo = {
      id: Date.now(),
      text: text.trim(),
      completed: false,
      createdAt: new Date()
    };
    
    this.setState(prevState => ({
      todos: [...prevState.todos, newTodo],
      error: null
    }));
  };
  
  handleToggleTodo = (id) => {
    this.setState(prevState => ({
      todos: prevState.todos.map(todo =>
        todo.id === id ? { ...todo, completed: !todo.completed } : todo
      )
    }));
  };
  
  handleDeleteTodo = (id) => {
    this.setState(prevState => ({
      todos: prevState.todos.filter(todo => todo.id !== id)
    }));
  };
  
  componentDidMount() {
    this.loadTodos();
    this.startAutoSave();
  }
  
  componentWillUnmount() {
    this.stopAutoSave();
  }
  
  loadTodos = async () => {
    this.setState({ isLoading: true });
    try {
      const todos = await this.fetchTodos();
      this.setState({ todos, isLoading: false });
    } catch (error) {
      this.setState({ error: error.message, isLoading: false });
    }
  };
  
  startAutoSave = () => {
    this.autoSaveTimer = setInterval(() => {
      this.saveTodos();
    }, this.autoSaveInterval);
  };
  
  stopAutoSave = () => {
    if (this.autoSaveTimer) {
      clearInterval(this.autoSaveTimer);
    }
  };
}

5. 静态方法和属性(Static):类级别的功能

🔍 应用场景

使用static关键字创建属于类本身而非实例的方法和属性,适用于工具函数、常量定义和工厂方法。

❌ 常见问题

静态方法使用中的常见误区:

// ❌ 错误示例:静态方法的误用
class MathUtils {
  constructor() {
    // 问题1:在静态工具类中使用构造函数
    this.precision = 2;
  }
  
  // 问题2:应该是静态的方法却定义为实例方法
  calculateDistance(x1, y1, x2, y2) {
    return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
  }
  
  // 问题3:静态方法中访问实例属性
  static formatNumber(num) {
    return num.toFixed(this.precision); // TypeError: Cannot read property 'precision'
  }
}

// 使用时需要创建实例,不合理
const utils = new MathUtils();
const distance = utils.calculateDistance(0, 0, 3, 4);

✅ 推荐方案

正确使用静态方法和属性:

/**
 * 数学工具类 - 正确的静态方法实现
 * @description 提供各种数学计算的静态工具方法
 */
class MathUtils {
  // ✅ 静态常量
  static PI = Math.PI;
  static E = Math.E;
  static GOLDEN_RATIO = (1 + Math.sqrt(5)) / 2;
  
  // ✅ 静态配置
  static DEFAULT_PRECISION = 2;
  static MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER;
  
  // ✅ 私有静态字段
  static #conversionFactors = {
    degrees: Math.PI / 180,
    radians: 180 / Math.PI
  };
  
  /**
   * 计算两点间距离
   * @param {number} x1 - 第一个点的x坐标
   * @param {number} y1 - 第一个点的y坐标
   * @param {number} x2 - 第二个点的x坐标
   * @param {number} y2 - 第二个点的y坐标
   * @returns {number} 距离值
   */
  static calculateDistance(x1, y1, x2, y2) {
    const dx = x2 - x1;
    const dy = y2 - y1;
    return Math.sqrt(dx * dx + dy * dy);
  }
  
  /**
   * 格式化数字
   * @param {number} num - 要格式化的数字
   * @param {number} precision - 精度,默认使用类的默认精度
   * @returns {string} 格式化后的数字字符串
   */
  static formatNumber(num, precision = MathUtils.DEFAULT_PRECISION) {
    if (typeof num !== 'number' || isNaN(num)) {
      throw new Error('Invalid number provided');
    }
    return num.toFixed(precision);
  }
  
  /**
   * 角度转弧度
   * @param {number} degrees - 角度值
   * @returns {number} 弧度值
   */
  static degreesToRadians(degrees) {
    return degrees * MathUtils.#conversionFactors.degrees;
  }
  
  /**
   * 弧度转角度
   * @param {number} radians - 弧度值
   * @returns {number} 角度值
   */
  static radiansToDegrees(radians) {
    return radians * MathUtils.#conversionFactors.radians;
  }
  
  /**
   * 计算斐波那契数列
   * @param {number} n - 序列位置
   * @returns {number} 斐波那契数
   */
  static fibonacci(n) {
    if (n < 0) throw new Error('n must be non-negative');
    if (n <= 1) return n;
    
    let a = 0, b = 1;
    for (let i = 2; i <= n; i++) {
      [a, b] = [b, a + b];
    }
    return b;
  }
  
  /**
   * 检查是否为质数
   * @param {number} num - 要检查的数字
   * @returns {boolean} 是否为质数
   */
  static isPrime(num) {
    if (num < 2) return false;
    if (num === 2) return true;
    if (num % 2 === 0) return false;
    
    for (let i = 3; i <= Math.sqrt(num); i += 2) {
      if (num % i === 0) return false;
    }
    return true;
  }
  
  /**
   * 生成指定范围内的随机整数
   * @param {number} min - 最小值(包含)
   * @param {number} max - 最大值(包含)
   * @returns {number} 随机整数
   */
  static randomInt(min, max) {
    if (min > max) [min, max] = [max, min];
    return Math.floor(Math.random() * (max - min + 1)) + min;
  }
  
  /**
   * 计算数组的统计信息
   * @param {number[]} numbers - 数字数组
   * @returns {Object} 统计信息对象
   */
  static getStatistics(numbers) {
    if (!Array.isArray(numbers) || numbers.length === 0) {
      throw new Error('Invalid or empty array provided');
    }
    
    const sorted = [...numbers].sort((a, b) => a - b);
    const sum = numbers.reduce((acc, num) => acc + num, 0);
    const mean = sum / numbers.length;
    
    return {
      count: numbers.length,
      sum,
      mean,
      median: MathUtils.#calculateMedian(sorted),
      mode: MathUtils.#calculateMode(numbers),
      min: sorted[0],
      max: sorted[sorted.length - 1],
      range: sorted[sorted.length - 1] - sorted[0],
      variance: MathUtils.#calculateVariance(numbers, mean),
      standardDeviation: Math.sqrt(MathUtils.#calculateVariance(numbers, mean))
    };
  }
  
  /**
   * 私有静态方法 - 计算中位数
   * @param {number[]} sortedNumbers - 已排序的数字数组
   * @returns {number} 中位数
   */
  static #calculateMedian(sortedNumbers) {
    const mid = Math.floor(sortedNumbers.length / 2);
    return sortedNumbers.length % 2 === 0
      ? (sortedNumbers[mid - 1] + sortedNumbers[mid]) / 2
      : sortedNumbers[mid];
  }
  
  /**
   * 私有静态方法 - 计算众数
   * @param {number[]} numbers - 数字数组
   * @returns {number[]} 众数数组
   */
  static #calculateMode(numbers) {
    const frequency = new Map();
    let maxFreq = 0;
    
    numbers.forEach(num => {
      const freq = (frequency.get(num) || 0) + 1;
      frequency.set(num, freq);
      maxFreq = Math.max(maxFreq, freq);
    });
    
    return [...frequency.entries()]
      .filter(([, freq]) => freq === maxFreq)
      .map(([num]) => num);
  }
  
  /**
   * 私有静态方法 - 计算方差
   * @param {number[]} numbers - 数字数组
   * @param {number} mean - 平均值
   * @returns {number} 方差
   */
  static #calculateVariance(numbers, mean) {
    const squaredDiffs = numbers.map(num => (num - mean) ** 2);
    return squaredDiffs.reduce((acc, diff) => acc + diff, 0) / numbers.length;
  }
  
  // ✅ 防止实例化工具类
  constructor() {
    throw new Error('MathUtils is a static class and cannot be instantiated');
  }
}

/**
 * 用户工厂类 - 展示静态工厂方法模式
 * @description 使用静态方法创建不同类型的用户实例
 */
class UserFactory {
  // ✅ 静态配置
  static DEFAULT_ROLES = ['user', 'admin', 'moderator'];
  static PASSWORD_MIN_LENGTH = 8;
  
  // ✅ 静态计数器
  static #userCount = 0;
  static #adminCount = 0;
  
  /**
   * 创建普通用户
   * @param {string} name - 用户名
   * @param {string} email - 邮箱
   * @param {Object} options - 可选配置
   * @returns {User} 用户实例
   */
  static createUser(name, email, options = {}) {
    const user = new User(name, email, {
      ...options,
      role: 'user',
      id: UserFactory.#generateUserId()
    });
    
    UserFactory.#userCount++;
    return user;
  }
  
  /**
   * 创建管理员用户
   * @param {string} name - 用户名
   * @param {string} email - 邮箱
   * @param {Array} permissions - 权限列表
   * @returns {User} 管理员用户实例
   */
  static createAdmin(name, email, permissions = []) {
    const admin = new User(name, email, {
      role: 'admin',
      permissions: ['read', 'write', 'delete', ...permissions],
      id: UserFactory.#generateAdminId()
    });
    
    UserFactory.#adminCount++;
    return admin;
  }
  
  /**
   * 从JSON数据创建用户
   * @param {Object} userData - 用户数据
   * @returns {User} 用户实例
   */
  static fromJSON(userData) {
    const { name, email, role, ...options } = userData;
    
    switch (role) {
      case 'admin':
        return UserFactory.createAdmin(name, email, options.permissions);
      case 'user':
      default:
        return UserFactory.createUser(name, email, options);
    }
  }
  
  /**
   * 批量创建用户
   * @param {Array} usersData - 用户数据数组
   * @returns {Array} 用户实例数组
   */
  static createBatch(usersData) {
    return usersData.map(userData => UserFactory.fromJSON(userData));
  }
  
  /**
   * 获取用户统计信息
   * @returns {Object} 统计信息
   */
  static getStatistics() {
    return {
      totalUsers: UserFactory.#userCount,
      totalAdmins: UserFactory.#adminCount,
      totalCount: UserFactory.#userCount + UserFactory.#adminCount
    };
  }
  
  /**
   * 私有静态方法 - 生成用户ID
   * @returns {string} 用户ID
   */
  static #generateUserId() {
    return `user_${Date.now()}_${UserFactory.#userCount + 1}`;
  }
  
  /**
   * 私有静态方法 - 生成管理员ID
   * @returns {string} 管理员ID
   */
  static #generateAdminId() {
    return `admin_${Date.now()}_${UserFactory.#adminCount + 1}`;
  }
  
  /**
   * 验证用户数据
   * @param {Object} userData - 用户数据
   * @returns {boolean} 验证结果
   */
  static validateUserData(userData) {
    const { name, email, password } = userData;
    
    if (!name || typeof name !== 'string' || name.trim().length === 0) {
      return false;
    }
    
    if (!email || !UserFactory.#isValidEmail(email)) {
      return false;
    }
    
    if (password && password.length < UserFactory.PASSWORD_MIN_LENGTH) {
      return false;
    }
    
    return true;
  }
  
  /**
   * 私有静态方法 - 邮箱验证
   * @param {string} email - 邮箱地址
   * @returns {boolean} 是否有效
   */
  static #isValidEmail(email) {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return emailRegex.test(email);
  }
}

💡 核心要点

  • 类级别访问:静态成员属于类本身,不需要实例化
  • 工具函数:适合创建不依赖实例状态的工具方法
  • 工厂模式:静态方法常用于实现工厂模式
  • 私有静态:可以结合私有字段创建真正私有的静态成员

🎯 实际应用

在配置管理中的静态方法应用:

// 实际项目中的配置管理应用
class ConfigManager {
  // 静态配置存储
  static #configs = new Map();
  static #defaultConfig = {
    theme: 'light',
    language: 'zh-CN',
    apiTimeout: 5000,
    retryAttempts: 3
  };
  
  // 环境配置
  static #environments = {
    development: {
      apiUrl: 'http://localhost:3000',
      debug: true,
      logLevel: 'debug'
    },
    production: {
      apiUrl: 'https://api.example.com',
      debug: false,
      logLevel: 'error'
    }
  };
  
  /**
   * 初始化配置
   * @param {string} environment - 环境名称
   */
  static initialize(environment = 'development') {
    const envConfig = ConfigManager.#environments[environment] || {};
    const config = { ...ConfigManager.#defaultConfig, ...envConfig };
    
    ConfigManager.#configs.set('global', config);
    console.log(`Configuration initialized for ${environment} environment`);
  }
  
  /**
   * 获取配置值
   * @param {string} key - 配置键
   * @param {string} namespace - 命名空间
   * @returns {any} 配置值
   */
  static get(key, namespace = 'global') {
    const config = ConfigManager.#configs.get(namespace);
    return config ? config[key] : undefined;
  }
  
  /**
   * 设置配置值
   * @param {string} key - 配置键
   * @param {any} value - 配置值
   * @param {string} namespace - 命名空间
   */
  static set(key, value, namespace = 'global') {
    if (!ConfigManager.#configs.has(namespace)) {
      ConfigManager.#configs.set(namespace, {});
    }
    
    const config = ConfigManager.#configs.get(namespace);
    config[key] = value;
  }
  
  /**
   * 获取所有配置
   * @param {string} namespace - 命名空间
   * @returns {Object} 配置对象
   */
  static getAll(namespace = 'global') {
    return { ...ConfigManager.#configs.get(namespace) };
  }
}

6. 静态初始化块(Static Initialization Blocks):高级初始化

🔍 应用场景

使用static {}块在类加载时执行复杂的静态初始化逻辑,适用于需要复杂计算或异步操作的静态属性初始化。

❌ 常见问题

传统的静态初始化方式存在局限:

// ❌ 传统方式:静态初始化的局限性
class DatabaseConnection {
  // 问题1:无法进行复杂的初始化逻辑
  static connectionPool = new Map(); // 简单初始化
  
  // 问题2:需要额外的初始化方法
  static initializePool() {
    // 复杂的初始化逻辑需要手动调用
    for (let i = 0; i < 10; i++) {
      this.connectionPool.set(`conn_${i}`, {
        id: `conn_${i}`,
        isActive: false,
        createdAt: new Date()
      });
    }
  }
  
  // 问题3:无法保证初始化顺序
  static maxConnections = DatabaseConnection.connectionPool.size; // 可能为0
}

// 需要手动调用初始化
DatabaseConnection.initializePool();

✅ 推荐方案

使用静态初始化块实现复杂的类初始化:

/**
 * 数据库连接池类 - 使用静态初始化块
 * @description 展示静态初始化块的高级用法
 */
class DatabaseConnection {
  // ✅ 静态字段声明
  static connectionPool = new Map();
  static availableConnections = [];
  static busyConnections = new Set();
  static maxConnections = 10;
  static minConnections = 2;
  static connectionTimeout = 30000;
  static retryAttempts = 3;
  
  // ✅ 配置对象
  static config = {};
  static statistics = {};
  static eventListeners = new Map();
  
  // ✅ 静态初始化块 - 复杂的初始化逻辑
  static {
    console.log('Initializing DatabaseConnection class...');
    
    // 初始化配置
    this.config = {
      host: process.env.DB_HOST || 'localhost',
      port: parseInt(process.env.DB_PORT) || 5432,
      database: process.env.DB_NAME || 'myapp',
      ssl: process.env.NODE_ENV === 'production',
      poolSize: this.maxConnections,
      idleTimeout: this.connectionTimeout,
      acquireTimeout: 10000
    };
    
    // 初始化统计信息
    this.statistics = {
      totalConnections: 0,
      activeConnections: 0,
      totalQueries: 0,
      failedConnections: 0,
      averageResponseTime: 0,
      startTime: new Date()
    };
    
    // 创建初始连接池
    this.initializeConnectionPool();
    
    // 设置定时清理任务
    this.setupCleanupTasks();
    
    // 注册进程退出处理
    this.registerExitHandlers();
    
    console.log(`DatabaseConnection initialized with ${this.connectionPool.size} connections`);
  }
  
  /**
   * 静态初始化块中调用的初始化方法
   */
  static initializeConnectionPool() {
    // 创建最小数量的连接
    for (let i = 0; i < this.minConnections; i++) {
      const connection = this.createConnection(`conn_${i}`);
      this.connectionPool.set(connection.id, connection);
      this.availableConnections.push(connection.id);
    }
    
    this.statistics.totalConnections = this.connectionPool.size;
  }
  
  /**
   * 创建数据库连接
   * @param {string} id - 连接ID
   * @returns {Object} 连接对象
   */
  static createConnection(id) {
    return {
      id,
      isActive: false,
      createdAt: new Date(),
      lastUsed: null,
      queryCount: 0,
      errorCount: 0,
      status: 'idle'
    };
  }
  
  /**
   * 设置清理任务
   */
  static setupCleanupTasks() {
    // 每30秒检查空闲连接
    setInterval(() => {
      this.cleanupIdleConnections();
    }, 30000);
    
    // 每5分钟更新统计信息
    setInterval(() => {
      this.updateStatistics();
    }, 300000);
  }
  
  /**
   * 注册进程退出处理
   */
  static registerExitHandlers() {
    const cleanup = () => {
      console.log('Closing database connections...');
      this.closeAllConnections();
    };
    
    process.on('SIGINT', cleanup);
    process.on('SIGTERM', cleanup);
    process.on('exit', cleanup);
  }
  
  /**
   * 获取可用连接
   * @returns {Promise<string>} 连接ID
   */
  static async getConnection() {
    // 如果有可用连接,直接返回
    if (this.availableConnections.length > 0) {
      const connectionId = this.availableConnections.pop();
      this.busyConnections.add(connectionId);
      
      const connection = this.connectionPool.get(connectionId);
      connection.isActive = true;
      connection.lastUsed = new Date();
      connection.status = 'active';
      
      this.statistics.activeConnections++;
      return connectionId;
    }
    
    // 如果连接池未满,创建新连接
    if (this.connectionPool.size < this.maxConnections) {
      const newId = `conn_${this.connectionPool.size}`;
      const connection = this.createConnection(newId);
      
      this.connectionPool.set(newId, connection);
      this.busyConnections.add(newId);
      
      connection.isActive = true;
      connection.lastUsed = new Date();
      connection.status = 'active';
      
      this.statistics.totalConnections++;
      this.statistics.activeConnections++;
      
      return newId;
    }
    
    // 等待连接可用
    return this.waitForConnection();
  }
  
  /**
   * 等待连接可用
   * @returns {Promise<string>} 连接ID
   */
  static waitForConnection() {
    return new Promise((resolve, reject) => {
      const timeout = setTimeout(() => {
        reject(new Error('Connection acquisition timeout'));
      }, this.config.acquireTimeout);
      
      const checkConnection = () => {
      if (this.availableConnections.length > 0) {
        clearTimeout(timeout);
        resolve(this.getConnection());
      } else {
        setTimeout(checkConnection, 100);
      }
    };
    
    checkConnection();
  });
}

/**
 * 释放连接
 * @param {string} connectionId - 连接ID
 */
static releaseConnection(connectionId) {
  if (this.busyConnections.has(connectionId)) {
    this.busyConnections.delete(connectionId);
    this.availableConnections.push(connectionId);
    
    const connection = this.connectionPool.get(connectionId);
    if (connection) {
      connection.isActive = false;
      connection.status = 'idle';
      this.statistics.activeConnections--;
    }
  }
}

/**
 * 清理空闲连接
 */
static cleanupIdleConnections() {
  const now = new Date();
  const idleThreshold = this.connectionTimeout;
  
  this.availableConnections.forEach((connectionId, index) => {
    const connection = this.connectionPool.get(connectionId);
    if (connection && connection.lastUsed) {
      const idleTime = now - connection.lastUsed;
      if (idleTime > idleThreshold && this.connectionPool.size > this.minConnections) {
        this.connectionPool.delete(connectionId);
        this.availableConnections.splice(index, 1);
        this.statistics.totalConnections--;
      }
    }
  });
}

/**
 * 更新统计信息
 */
static updateStatistics() {
  this.statistics.uptime = new Date() - this.statistics.startTime;
  this.statistics.poolUtilization = (this.busyConnections.size / this.maxConnections) * 100;
  
  console.log('Database Pool Statistics:', {
    totalConnections: this.statistics.totalConnections,
    activeConnections: this.statistics.activeConnections,
    poolUtilization: `${this.statistics.poolUtilization.toFixed(2)}%`,
    uptime: `${Math.floor(this.statistics.uptime / 1000)}s`
  });
}

/**
 * 关闭所有连接
 */
static closeAllConnections() {
  this.connectionPool.clear();
  this.availableConnections.length = 0;
  this.busyConnections.clear();
  this.statistics.totalConnections = 0;
  this.statistics.activeConnections = 0;
}

/**
 * 获取连接池状态
 * @returns {Object} 连接池状态
 */
static getPoolStatus() {
  return {
    total: this.connectionPool.size,
    available: this.availableConnections.length,
    busy: this.busyConnections.size,
    maxConnections: this.maxConnections,
    utilization: (this.busyConnections.size / this.maxConnections) * 100
  };
}
}

/**
 * 缓存管理类 - 展示多个静态初始化块
 * @description 使用多个静态初始化块进行分阶段初始化
 */
class CacheManager {
// ✅ 基础静态字段
static cache = new Map();
static config = {};
static metrics = {};

// ✅ 第一个静态初始化块 - 基础配置
static {
  console.log('Phase 1: Initializing basic cache configuration...');
  
  this.config = {
    maxSize: parseInt(process.env.CACHE_MAX_SIZE) || 1000,
    ttl: parseInt(process.env.CACHE_TTL) || 300000, // 5分钟
    cleanupInterval: parseInt(process.env.CACHE_CLEANUP_INTERVAL) || 60000, // 1分钟
    compressionEnabled: process.env.CACHE_COMPRESSION === 'true',
    persistenceEnabled: process.env.CACHE_PERSISTENCE === 'true'
  };
  
  console.log('Basic configuration loaded:', this.config);
}

// ✅ 第二个静态初始化块 - 指标初始化
static {
  console.log('Phase 2: Initializing cache metrics...');
  
  this.metrics = {
    hits: 0,
    misses: 0,
    sets: 0,
    deletes: 0,
    evictions: 0,
    totalSize: 0,
    startTime: new Date()
  };
  
  // 计算命中率的getter
  Object.defineProperty(this.metrics, 'hitRate', {
    get() {
      const total = this.hits + this.misses;
      return total > 0 ? (this.hits / total * 100).toFixed(2) : 0;
    }
  });
  
  console.log('Metrics system initialized');
}

// ✅ 第三个静态初始化块 - 高级功能
static {
  console.log('Phase 3: Setting up advanced cache features...');
  
  // 设置定时清理
  setInterval(() => {
    this.cleanup();
  }, this.config.cleanupInterval);
  
  // 设置指标报告
  setInterval(() => {
    this.reportMetrics();
  }, 300000); // 每5分钟报告一次
  
  // 如果启用持久化,加载缓存数据
  if (this.config.persistenceEnabled) {
    this.loadPersistedCache();
  }
  
  console.log('Advanced features initialized');
  console.log('CacheManager fully initialized and ready to use');
}

/**
 * 设置缓存项
 * @param {string} key - 缓存键
 * @param {any} value - 缓存值
 * @param {number} customTtl - 自定义TTL
 */
static set(key, value, customTtl = null) {
  const ttl = customTtl || this.config.ttl;
  const expiresAt = new Date(Date.now() + ttl);
  
  // 如果缓存已满,执行LRU淘汰
  if (this.cache.size >= this.config.maxSize && !this.cache.has(key)) {
    this.evictLRU();
  }
  
  const cacheItem = {
    value,
    expiresAt,
    accessCount: 0,
    lastAccessed: new Date(),
    size: this.calculateSize(value)
  };
  
  this.cache.set(key, cacheItem);
  this.metrics.sets++;
  this.updateTotalSize();
}

/**
 * 获取缓存项
 * @param {string} key - 缓存键
 * @returns {any} 缓存值或undefined
 */
static get(key) {
  const item = this.cache.get(key);
  
  if (!item) {
    this.metrics.misses++;
    return undefined;
  }
  
  // 检查是否过期
  if (new Date() > item.expiresAt) {
    this.cache.delete(key);
    this.metrics.misses++;
    this.updateTotalSize();
    return undefined;
  }
  
  // 更新访问信息
  item.accessCount++;
  item.lastAccessed = new Date();
  this.metrics.hits++;
  
  return item.value;
}

/**
 * LRU淘汰策略
 */
static evictLRU() {
  let oldestKey = null;
  let oldestTime = new Date();
  
  for (const [key, item] of this.cache) {
    if (item.lastAccessed < oldestTime) {
      oldestTime = item.lastAccessed;
      oldestKey = key;
    }
  }
  
  if (oldestKey) {
    this.cache.delete(oldestKey);
    this.metrics.evictions++;
    this.updateTotalSize();
  }
}

/**
 * 清理过期项
 */
static cleanup() {
  const now = new Date();
  let cleanedCount = 0;
  
  for (const [key, item] of this.cache) {
    if (now > item.expiresAt) {
      this.cache.delete(key);
      cleanedCount++;
    }
  }
  
  if (cleanedCount > 0) {
    this.updateTotalSize();
    console.log(`Cleaned up ${cleanedCount} expired cache items`);
  }
}

/**
 * 计算值的大小
 * @param {any} value - 要计算的值
 * @returns {number} 大小(字节)
 */
static calculateSize(value) {
  return JSON.stringify(value).length * 2; // 简化的大小计算
}

/**
 * 更新总大小
 */
static updateTotalSize() {
  this.metrics.totalSize = Array.from(this.cache.values())
    .reduce((total, item) => total + item.size, 0);
}

/**
 * 报告指标
 */
static reportMetrics() {
  console.log('Cache Metrics Report:', {
    hitRate: `${this.metrics.hitRate}%`,
    totalItems: this.cache.size,
    totalSize: `${(this.metrics.totalSize / 1024).toFixed(2)} KB`,
    hits: this.metrics.hits,
    misses: this.metrics.misses,
    evictions: this.metrics.evictions
  });
}

/**
 * 加载持久化缓存
 */
static loadPersistedCache() {
  // 实际项目中会从文件或数据库加载
  console.log('Loading persisted cache data...');
}
}

💡 核心要点

  • 复杂初始化:静态初始化块支持复杂的初始化逻辑
  • 执行顺序:多个静态初始化块按声明顺序执行
  • 异步支持:可以在初始化块中设置异步任务
  • 私有访问:可以访问私有静态字段和方法

🎯 实际应用

在微服务配置中的静态初始化块应用:

// 实际项目中的微服务配置应用
class ServiceRegistry {
  static services = new Map();
  static healthChecks = new Map();
  static loadBalancer = null;
  
  static {
    // 初始化服务发现
    this.initializeServiceDiscovery();
    
    // 设置健康检查
    this.setupHealthChecks();
    
    // 配置负载均衡
    this.configureLoadBalancer();
    
    console.log('ServiceRegistry initialized with service discovery');
  }
  
  static initializeServiceDiscovery() {
    // 从环境变量或配置文件加载服务
    const serviceConfig = process.env.SERVICES_CONFIG || '[]';
    const services = JSON.parse(serviceConfig);
    
    services.forEach(service => {
      this.registerService(service.name, service.endpoints);
    });
  }
  
  static registerService(name, endpoints) {
    this.services.set(name, {
      endpoints: endpoints.map(ep => ({ ...ep, healthy: true })),
      lastCheck: new Date(),
      requestCount: 0
    });
  }
  
  static setupHealthChecks() {
    setInterval(() => {
      this.performHealthChecks();
    }, 30000); // 每30秒检查一次
  }
  
  static async performHealthChecks() {
    for (const [serviceName, service] of this.services) {
      for (const endpoint of service.endpoints) {
        try {
          const response = await fetch(`${endpoint.url}/health`, {
            timeout: 5000
          });
          endpoint.healthy = response.ok;
        } catch (error) {
          endpoint.healthy = false;
        }
      }
    }
  }
}

📊 特性对比总结

特性 使用场景 优势 注意事项
构造函数 实例初始化 自动调用、参数验证 必须同步、避免复杂逻辑
extends继承 代码复用、多态 层次结构清晰、方法重写 避免过深继承、正确使用super
私有字段 数据封装 真正私有、编译时检查 不能被继承、语法较新
公有字段 属性声明 结构清晰、自动绑定 实例属性、默认值设置
static方法 工具函数、工厂 无需实例化、类级别访问 不能访问实例属性
静态初始化块 复杂初始化 支持复杂逻辑、执行顺序 语法较新、浏览器兼容性

🎯 实际应用建议

1. 选择合适的特性组合

// ✅ 推荐的特性组合使用
class ModernComponent {
  // 公有字段声明
  state = { loading: false };
  
  // 私有字段保护敏感数据
  #apiKey = process.env.API_KEY;
  
  // 静态工具方法
  static createInstance(config) {
    return new ModernComponent(config);
  }
  
  // 构造函数处理必需参数
  constructor(config) {
    this.config = { ...this.defaultConfig, ...config };
  }
  
  // 实例方法使用箭头函数自动绑定
  handleAction = async () => {
    this.state.loading = true;
    try {
      await this.performAction();
    } finally {
      this.state.loading = false;
    }
  };
}

2. 性能优化考虑

  • 静态方法:用于不依赖实例状态的工具函数
  • 私有字段:比WeakMap方式性能更好
  • 公有字段:避免在构造函数中重复声明

3. 代码组织最佳实践

  • 分离关注点:构造函数只做初始化,复杂逻辑分离到其他方法
  • 合理继承:避免过深的继承链,优先组合而非继承
  • 静态初始化:用于一次性的复杂设置和配置

🎉 总结

JavaScript类的现代特性为我们提供了强大的面向对象编程能力:

  1. 构造函数提供了可靠的实例初始化机制
  2. extends继承实现了优雅的代码复用和扩展
  3. 私有字段带来了真正的数据封装和安全性
  4. 公有字段简化了属性声明和类结构
  5. static特性提供了类级别的功能和工具方法
  6. 静态初始化块支持复杂的类初始化逻辑

掌握这些特性,你就能写出更加专业、安全、高效的JavaScript代码。在实际项目中,合理组合使用这些特性,将大大提升代码的可维护性和开发效率!


🔗 相关资源

❌