JavaScript 开发的最佳实践涵盖代码组织、性能优化、安全性等多个方面。
// utils/math.js
export function add(a, b) {
return a + b
}
export function multiply(a, b) {
return a * b
}
export class Calculator {
static add(a, b) {
return a + b
}
}
// utils/validation.js
export function isValidEmail(email) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
return regex.test(email)
}
export function isValidPassword(password) {
return password.length >= 8
}
// main.js
import { add, multiply, Calculator } from './utils/math.js'
import { isValidEmail, isValidPassword } from './utils/validation.js'
console.log(add(2, 3)) // 5
console.log(Calculator.add(4, 5)) // 9
console.log(isValidEmail('test@example.com')) // true// 不好的做法:一个函数做太多事情
function processUserData(data) {
// 验证数据
if (!data.name || !data.email) {
throw new Error('Invalid data')
}
// 保存到数据库
saveToDatabase(data)
// 发送通知邮件
sendNotificationEmail(data.email)
// 更新 UI
updateUI(data)
}
// 好的做法:拆分为多个专门的函数
function validateUserData(data) {
if (!data.name || !data.email) {
throw new Error('Invalid data')
}
}
function saveUserData(data) {
return saveToDatabase(data)
}
function notifyUser(data) {
return sendNotificationEmail(data.email)
}
function updateUserInterface(data) {
updateUI(data)
}
function processUserData(data) {
validateUserData(data)
const savedData = saveUserData(data)
notifyUser(savedData)
updateUserInterface(savedData)
return savedData
}// 防抖:延迟执行,直到停止触发
function debounce(func, delay) {
let timeoutId
return function(...args) {
clearTimeout(timeoutId)
timeoutId = setTimeout(() => func.apply(this, args), delay)
}
}
// 节流:限制执行频率
function throttle(func, delay) {
let lastCall = 0
return function(...args) {
const now = Date.now()
if (now - lastCall >= delay) {
lastCall = now
func.apply(this, args)
}
}
}
// 使用示例
const debouncedSearch = debounce(searchFunction, 300)
const throttledScroll = throttle(scrollHandler, 16)
input.addEventListener('input', debouncedSearch)
window.addEventListener('scroll', throttledScroll)// 避免内存泄漏
class DataManager {
constructor() {
this.data = new Map()
this.listeners = new Set()
}
addData(key, value) {
this.data.set(key, value)
this.notifyListeners('add', { key, value })
}
removeData(key) {
if (this.data.has(key)) {
this.data.delete(key)
this.notifyListeners('remove', { key })
}
}
addListener(callback) {
this.listeners.add(callback)
}
removeListener(callback) {
this.listeners.delete(callback)
}
notifyListeners(event, data) {
for (let listener of this.listeners) {
try {
listener(event, data)
} catch (error) {
console.error('Listener error:', error)
}
}
}
destroy() {
// 清理所有引用
this.data.clear()
this.listeners.clear()
}
}
// 正确使用
const manager = new DataManager()
// 添加监听器
const listener = (event, data) => {
console.log(event, data)
}
manager.addListener(listener)
// 使用完毕后清理
manager.removeListener(listener)
manager.destroy()// 图片懒加载
class LazyImageLoader {
constructor(images) {
this.images = images
this.observer = null
this.init()
}
init() {
if ('IntersectionObserver' in window) {
this.observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
this.loadImage(entry.target)
this.observer.unobserve(entry.target)
}
})
})
this.images.forEach(img => {
this.observer.observe(img)
})
} else {
// 降级方案:滚动监听
this.fallbackLoad()
}
}
loadImage(img) {
const src = img.dataset.src
if (src) {
img.src = src
img.classList.remove('lazy')
}
}
fallbackLoad() {
const loadImages = () => {
this.images.forEach(img => {
if (this.isInViewport(img)) {
this.loadImage(img)
}
})
}
window.addEventListener('scroll', loadImages)
window.addEventListener('resize', loadImages)
loadImages() // 初始加载
}
isInViewport(element) {
const rect = element.getBoundingClientRect()
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
)
}
}
// 使用示例
document.addEventListener('DOMContentLoaded', () => {
const lazyImages = document.querySelectorAll('img[data-src]')
new LazyImageLoader(lazyImages)
})// HTML 转义
function escapeHtml(text) {
const div = document.createElement('div')
div.textContent = text
return div.innerHTML
}
// URL 验证
function isValidUrl(url) {
try {
new URL(url)
return true
} catch {
return false
}
}
// 防止 XSS
function sanitizeInput(input) {
// 移除危险的 HTML 标签和属性
return input
.replace(/<script[^>]*>.*?<\/script>/gi, '')
.replace(/<[^>]*>/g, '')
.trim()
}
// SQL 注入防护(客户端)
function sanitizeSqlInput(input) {
// 移除或转义危险字符
return input.replace(/['";\\]/g, '\\$&')
}
// CSRF 防护
function addCsrfToken(headers = {}) {
const token = document.querySelector('meta[name="csrf-token"]')?.content
if (token) {
headers['X-CSRF-Token'] = token
}
return headers
}
// 使用示例
fetch('/api/user', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...addCsrfToken()
},
body: JSON.stringify({
name: sanitizeInput(userName),
email: sanitizeInput(userEmail)
})
})<!-- CSP 头部 -->
<meta http-equiv="Content-Security-Policy" content="
default-src 'self';
script-src 'self' 'unsafe-inline' https://trusted-cdn.com;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' data: https:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.example.com;
frame-src 'none';
">// 动态设置 CSP(有限支持)
if ('csp' in document) {
document.csp = "default-src 'self'"
}// 同步错误
window.onerror = function(message, source, lineno, colno, error) {
console.error('Global error:', {
message,
source,
lineno,
colno,
stack: error?.stack
})
// 发送错误报告
reportError({
message,
source,
lineno,
colno,
stack: error?.stack,
userAgent: navigator.userAgent,
url: window.location.href,
timestamp: Date.now()
})
// 阻止默认错误处理
return true
}
// Promise 拒绝
window.onunhandledrejection = function(event) {
console.error('Unhandled promise rejection:', event.reason)
reportError({
type: 'unhandledrejection',
reason: event.reason,
timestamp: Date.now()
})
// 阻止默认处理
event.preventDefault()
}
// 资源加载错误
window.addEventListener('error', function(event) {
if (event.target !== window) {
console.error('Resource load error:', {
src: event.target.src || event.target.href,
tagName: event.target.tagName
})
}
}, true)// 特性检测和降级
function setupFeature() {
if ('geolocation' in navigator) {
// 使用原生地理位置 API
navigator.geolocation.getCurrentPosition(success, error)
} else {
// 降级方案
console.log('Geolocation not supported, using fallback')
useFallbackGeolocation()
}
}
// API 兼容性处理
function createCompatibleXHR() {
if (typeof XMLHttpRequest !== 'undefined') {
return new XMLHttpRequest()
} else if (typeof ActiveXObject !== 'undefined') {
return new ActiveXObject('Microsoft.XMLHTTP')
} else {
throw new Error('XMLHttpRequest not supported')
}
}
// 渐进增强
function enhanceUI() {
const button = document.getElementById('submit-btn')
// 基础功能
button.addEventListener('click', basicSubmit)
// 增强功能
if ('FormData' in window) {
button.addEventListener('click', enhancedSubmit)
}
if ('serviceWorker' in navigator) {
setupOfflineSupport()
}
}// 简单的测试框架
class TestSuite {
constructor(name) {
this.name = name
this.tests = []
this.passed = 0
this.failed = 0
}
test(name, fn) {
this.tests.push({ name, fn })
}
assert(condition, message = 'Assertion failed') {
if (!condition) {
throw new Error(message)
}
}
async run() {
console.group(`Running test suite: ${this.name}`)
for (let test of this.tests) {
try {
await test.fn.call(this)
console.log(`✓ ${test.name}`)
this.passed++
} catch (error) {
console.error(`✗ ${test.name}: ${error.message}`)
this.failed++
}
}
console.log(`\nResults: ${this.passed} passed, ${this.failed} failed`)
console.groupEnd()
return { passed: this.passed, failed: this.failed }
}
}
// 使用示例
const mathTests = new TestSuite('Math functions')
mathTests.test('Addition', function() {
this.assert(add(2, 3) === 5, '2 + 3 should equal 5')
this.assert(add(-1, 1) === 0, '-1 + 1 should equal 0')
})
mathTests.test('Multiplication', function() {
this.assert(multiply(3, 4) === 12, '3 * 4 should equal 12')
this.assert(multiply(0, 5) === 0, '0 * 5 should equal 0')
})
mathTests.run()JavaScript 最佳实践涵盖了开发的全过程:
遵循这些最佳实践可以提高代码质量、可维护性和安全性。