1. 为什么JavaScript是网页的灵魂?
2005年,当Google Maps首次实现无刷新拖拽地图时,整个互联网为之震动。这背后正是JavaScript的魔力——它让静态网页"活"了起来。如今,从简单的表单验证到复杂的单页应用,JavaScript已成为前端开发不可替代的核心技术。
JavaScript最初由网景公司的Brendan Eich在10天内设计完成,这个匆忙诞生的语言如今却支撑着现代Web的交互体系。与HTML负责结构、CSS负责样式不同,JavaScript赋予了网页动态行为和逻辑处理能力。当用户点击按钮时,是JavaScript在背后响应事件;当页面内容无刷新更新时,是JavaScript在异步获取数据;当看到精美的动画效果时,也是JavaScript在操控DOM元素的属性变化。
提示:现代前端开发中,虽然出现了TypeScript等超集语言,但最终它们都会被编译成JavaScript在浏览器中执行。掌握原生JavaScript是理解所有前端框架的基础。
2. JavaScript基础语法精要
2.1 变量与数据类型
JavaScript作为弱类型语言,其变量声明方式经历了从var到let/const的演变。初学者常犯的错误是滥用var导致变量提升(hoisting)问题:
javascript复制console.log(x); // 输出undefined而不是报错
var x = 5;
使用let可以避免这种反直觉的行为:
javascript复制console.log(x); // ReferenceError
let x = 5;
JavaScript有7种原始数据类型:Undefined、Null、Boolean、Number、BigInt、String、Symbol,以及Object类型。类型转换是常见的坑点:
javascript复制console.log(1 + "1"); // "11" 字符串拼接
console.log("1" - 1); // 0 数字运算
2.2 函数与作用域
JavaScript函数是一等公民,可以作为参数传递或作为返回值。箭头函数(=>)与传统函数的关键区别在于this绑定:
javascript复制const obj = {
name: "test",
regularFunc: function() {
console.log(this.name); // "test"
},
arrowFunc: () => {
console.log(this.name); // undefined
}
};
闭包(closure)是JavaScript最强大的特性之一,它允许函数访问并记住其词法作用域:
javascript复制function createCounter() {
let count = 0;
return function() {
return ++count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
3. DOM操作:连接JavaScript与网页
3.1 选取与修改元素
Document Object Model (DOM) 是JavaScript与HTML交互的接口。基础选择方法包括:
javascript复制// 通过ID选择
const header = document.getElementById('header');
// 通过类名选择(返回集合)
const items = document.getElementsByClassName('item');
// 通过CSS选择器选择
const button = document.querySelector('.btn-primary');
const allButtons = document.querySelectorAll('button');
修改元素属性和内容:
javascript复制// 修改文本内容
element.textContent = '新内容';
// 修改HTML内容
element.innerHTML = '<strong>强调文本</strong>';
// 修改样式
element.style.color = 'red';
element.style.fontSize = '20px';
// 修改类名
element.classList.add('active');
element.classList.remove('inactive');
element.classList.toggle('hidden');
3.2 事件处理机制
事件是JavaScript实现交互的核心。常见的事件绑定方式:
javascript复制// 传统方式
button.onclick = function() {
console.log('按钮被点击');
};
// 现代推荐方式
button.addEventListener('click', function(event) {
console.log('事件类型:', event.type);
console.log('触发元素:', event.target);
});
事件冒泡与捕获是理解事件传播的关键:
html复制<div id="parent">
<button id="child">点击我</button>
</div>
<script>
document.getElementById('parent').addEventListener('click', function() {
console.log('父元素捕获阶段');
}, true); // 第三个参数为true表示捕获阶段
document.getElementById('parent').addEventListener('click', function() {
console.log('父元素冒泡阶段');
}); // 默认冒泡阶段
document.getElementById('child').addEventListener('click', function() {
console.log('子元素');
});
</script>
点击按钮后控制台输出顺序为:父元素捕获阶段 → 子元素 → 父元素冒泡阶段。
4. 现代JavaScript实战技巧
4.1 AJAX与Fetch API
传统XMLHttpRequest与现代fetch API对比:
javascript复制// 传统XHR方式
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data');
xhr.onload = function() {
if (xhr.status === 200) {
console.log(JSON.parse(xhr.response));
}
};
xhr.send();
// 现代fetch方式
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) throw new Error('网络响应不正常');
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('请求失败:', error));
4.2 ES6+新特性实践
解构赋值简化代码:
javascript复制const user = { name: 'Alice', age: 25, email: 'alice@example.com' };
const { name, email } = user;
console.log(name, email); // Alice alice@example.com
展开运算符的妙用:
javascript复制const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 }; // { a: 1, b: 2, c: 3 }
4.3 常见内存泄漏与性能优化
闭包导致的内存泄漏:
javascript复制// 问题代码
function setupLeak() {
const hugeArray = new Array(1000000).fill('*');
return function() {
console.log('这个闭包保留了hugeArray的引用');
};
}
// 解决方案
function setupNoLeak() {
const hugeArray = new Array(1000000).fill('*');
return function() {
const smallPart = hugeArray.slice(0, 10); // 只保留需要的部分
console.log('优化后的闭包');
hugeArray = null; // 手动解除引用
};
}
事件监听器忘记移除:
javascript复制// 问题代码
function setupElement() {
const button = document.getElementById('myButton');
button.addEventListener('click', onClick);
}
// 解决方案
function cleanSetup() {
const button = document.getElementById('myButton');
const handler = onClick;
button.addEventListener('click', handler);
// 需要移除时
button.removeEventListener('click', handler);
}
5. 从入门到实战:构建一个待办事项应用
5.1 项目结构与初始化
创建基础HTML结构:
html复制<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript待办事项</title>
<style>
.completed { text-decoration: line-through; opacity: 0.7; }
.task-list { margin: 20px 0; }
.task-item { padding: 8px; border-bottom: 1px solid #eee; }
</style>
</head>
<body>
<div id="app">
<h1>我的待办事项</h1>
<form id="task-form">
<input type="text" id="task-input" placeholder="输入新任务..." required>
<button type="submit">添加</button>
</form>
<ul id="task-list" class="task-list"></ul>
</div>
<script src="app.js"></script>
</body>
</html>
5.2 JavaScript功能实现
在app.js中添加核心逻辑:
javascript复制// 状态管理
let tasks = JSON.parse(localStorage.getItem('tasks')) || [];
// DOM元素
const taskForm = document.getElementById('task-form');
const taskInput = document.getElementById('task-input');
const taskList = document.getElementById('task-list');
// 渲染任务列表
function renderTasks() {
taskList.innerHTML = '';
tasks.forEach((task, index) => {
const li = document.createElement('li');
li.className = 'task-item';
if (task.completed) li.classList.add('completed');
li.innerHTML = `
<input type="checkbox" ${task.completed ? 'checked' : ''} data-id="${index}">
<span>${task.text}</span>
<button data-id="${index}">删除</button>
`;
taskList.appendChild(li);
});
// 保存到本地存储
localStorage.setItem('tasks', JSON.stringify(tasks));
}
// 添加新任务
taskForm.addEventListener('submit', e => {
e.preventDefault();
const text = taskInput.value.trim();
if (text) {
tasks.push({ text, completed: false });
taskInput.value = '';
renderTasks();
}
});
// 处理任务操作(委托给父元素)
taskList.addEventListener('click', e => {
const index = e.target.getAttribute('data-id');
if (e.target.matches('button')) {
// 删除任务
tasks.splice(index, 1);
renderTasks();
} else if (e.target.matches('input[type="checkbox"]')) {
// 切换完成状态
tasks[index].completed = e.target.checked;
renderTasks();
}
});
// 初始渲染
renderTasks();
5.3 功能扩展与优化
添加任务过滤功能:
javascript复制// 在HTML中添加过滤按钮
// <div class="filters">
// <button data-filter="all">全部</button>
// <button data-filter="active">未完成</button>
// <button data-filter="completed">已完成</button>
// </div>
document.querySelector('.filters').addEventListener('click', e => {
if (!e.target.matches('button')) return;
const filter = e.target.dataset.filter;
const items = taskList.querySelectorAll('.task-item');
items.forEach(item => {
const isCompleted = item.classList.contains('completed');
switch(filter) {
case 'active':
item.style.display = isCompleted ? 'none' : '';
break;
case 'completed':
item.style.display = isCompleted ? '' : 'none';
break;
default:
item.style.display = '';
}
});
});
添加动画效果:
css复制/* 在style标签中添加 */
.task-item {
transition: all 0.3s ease;
}
.task-item.fade-out {
opacity: 0;
transform: translateX(-100%);
}
javascript复制// 修改删除逻辑
taskList.addEventListener('click', e => {
if (e.target.matches('button')) {
const item = e.target.closest('.task-item');
item.classList.add('fade-out');
setTimeout(() => {
const index = e.target.getAttribute('data-id');
tasks.splice(index, 1);
renderTasks();
}, 300);
}
// 其他逻辑...
});
6. JavaScript调试与性能分析
6.1 Chrome开发者工具实战
Console的高级用法:
javascript复制// 使用console.table展示结构化数据
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 }
];
console.table(users);
// 性能计时
console.time('数据计算');
// 执行一些复杂计算
console.timeEnd('数据计算'); // 输出执行时间
断点调试技巧:
- 在Sources面板中找到你的JS文件
- 点击行号设置断点
- 触发相关代码执行
- 使用右侧面板查看调用栈、作用域变量等
- 使用单步执行按钮(→)逐步调试
6.2 性能优化实践
识别强制同步布局(Forced Synchronous Layout):
javascript复制// 问题代码 - 导致多次重排
const elements = document.querySelectorAll('.box');
for (let i = 0; i < elements.length; i++) {
elements[i].style.width = '100px'; // 写操作
console.log(elements[i].offsetWidth); // 读操作,强制同步布局
}
// 优化代码 - 分离读写
const widths = [];
for (let i = 0; i < elements.length; i++) {
elements[i].style.width = '100px'; // 批量写
}
for (let i = 0; i < elements.length; i++) {
widths.push(elements[i].offsetWidth); // 批量读
}
使用requestAnimationFrame优化动画:
javascript复制function animate() {
// 更新动画状态
element.style.transform = `translateX(${position}px)`;
// 继续下一帧
if (position < target) {
position += 5;
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
7. JavaScript进阶路线图
7.1 模块化开发
从IIFE到ES Modules的演进:
javascript复制// 传统IIFE方式
(function() {
const privateVar = '内部变量';
window.myModule = {
publicMethod: function() {
console.log(privateVar);
}
};
})();
// 现代ES Modules
// module.js
const privateVar = '内部变量';
export function publicMethod() {
console.log(privateVar);
}
// main.js
import { publicMethod } from './module.js';
publicMethod();
7.2 异步编程进阶
Promise链与错误处理:
javascript复制fetch('/api/data')
.then(response => {
if (!response.ok) throw new Error('网络响应错误');
return response.json();
})
.then(data => processData(data))
.catch(error => {
console.error('请求失败:', error);
return getFallbackData(); // 提供备用数据
})
.then(finalData => {
renderUI(finalData);
});
Async/Await最佳实践:
javascript复制async function loadUserData(userId) {
try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) throw new Error('用户数据获取失败');
const user = await response.json();
const postsResponse = await fetch(`/api/posts?userId=${userId}`);
const posts = await postsResponse.json();
return { user, posts };
} catch (error) {
console.error('加载数据时出错:', error);
throw error; // 重新抛出给上层处理
}
}
// 使用
loadUserData(123)
.then(data => console.log(data))
.catch(error => console.error('最终错误:', error));
7.3 面向现代前端的技术栈
TypeScript基础类型:
typescript复制interface User {
id: number;
name: string;
email?: string; // 可选属性
}
function greetUser(user: User): string {
return `Hello, ${user.name}`;
}
const newUser: User = {
id: 1,
name: 'Alice'
};
Web Components自定义元素:
javascript复制class MyCounter extends HTMLElement {
constructor() {
super();
this.count = 0;
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<style>
button { padding: 5px 10px; }
</style>
<button id="counter">0</button>
`;
}
connectedCallback() {
this.shadowRoot.getElementById('counter')
.addEventListener('click', () => {
this.count++;
this.shadowRoot.getElementById('counter').textContent = this.count;
});
}
}
customElements.define('my-counter', MyCounter);
在HTML中使用:
html复制<my-counter></my-counter>
8. 常见问题与解决方案
8.1 跨域问题(CORS)处理
开发环境解决方案:
javascript复制// 后端设置响应头(以Node.js Express为例)
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'http://localhost:3000');
res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});
// 前端fetch添加模式
fetch('http://api.example.com/data', {
mode: 'cors', // 默认值
credentials: 'include' // 如果需要发送cookie
});
生产环境推荐方案:
- 使用同源策略
- 设置反向代理(Nginx配置示例):
nginx复制location /api/ {
proxy_pass http://api-server:3000/;
proxy_set_header Host $host;
}
8.2 内存泄漏排查
Chrome内存分析步骤:
- 打开开发者工具 → Memory面板
- 拍摄堆快照(Heap snapshot)
- 执行可能泄漏的操作
- 再次拍摄堆快照
- 比较两次快照,查看对象增长情况
常见泄漏模式识别:
- 分离的DOM树:DOM节点从文档移除但仍被JS引用
- 闭包保持大对象引用
- 未清除的定时器或事件监听器
8.3 性能瓶颈定位
使用Performance面板记录和分析:
- 点击"Record"按钮
- 执行页面操作
- 停止记录
- 分析火焰图:
- 查找长任务(超过50ms的任务)
- 检查强制同步布局(黄色三角形标记)
- 识别频繁的垃圾回收(GC)
优化长任务策略:
- 将任务拆分为小块使用requestIdleCallback
- 使用Web Worker处理CPU密集型任务
- 避免在循环中进行DOM操作
9. 安全最佳实践
9.1 XSS防护
转义用户输入内容:
javascript复制function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
// 使用textContent而不是innerHTML
element.textContent = userInput;
内容安全策略(CSP)设置:
html复制<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' 'unsafe-inline'">
9.2 CSRF防护
使用Anti-CSRF Token:
html复制<form action="/submit" method="POST">
<input type="hidden" name="_csrf" value="随机token值">
<!-- 其他表单字段 -->
</form>
后端验证示例(Node.js):
javascript复制app.post('/submit', (req, res) => {
const csrfToken = req.body._csrf;
if (!validateCsrfToken(csrfToken)) {
return res.status(403).send('CSRF验证失败');
}
// 处理表单
});
9.3 安全HTTP头配置
推荐的安全头设置:
http复制Strict-Transport-Security: max-age=63072000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin
Express中间件设置示例:
javascript复制const helmet = require('helmet');
app.use(helmet());
10. 测试与质量保障
10.1 单元测试实践
使用Jest测试框架示例:
javascript复制// math.js
function sum(a, b) {
return a + b;
}
module.exports = { sum };
// math.test.js
const { sum } = require('./math');
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
test('浮点数相加', () => {
expect(sum(0.1, 0.2)).toBeCloseTo(0.3);
});
DOM测试示例(使用Jest和jsdom):
javascript复制// domUtils.js
function createButton(text) {
const btn = document.createElement('button');
btn.textContent = text;
return btn;
}
// domUtils.test.js
test('创建按钮元素', () => {
const button = createButton('点击我');
expect(button.textContent).toBe('点击我');
expect(button.tagName).toBe('BUTTON');
});
10.2 E2E测试方案
使用Cypress进行端到端测试:
javascript复制// todo.spec.js
describe('待办事项应用', () => {
it('成功添加任务', () => {
cy.visit('/');
cy.get('#task-input').type('新任务');
cy.get('#task-form').submit();
cy.get('.task-item').should('have.length', 1);
cy.contains('.task-item', '新任务');
});
it('标记任务为完成', () => {
// 先添加任务
cy.get('.task-item input[type="checkbox"]').click();
cy.get('.task-item').should('have.class', 'completed');
});
});
10.3 静态代码分析
ESLint配置示例(.eslintrc.js):
javascript复制module.exports = {
env: {
browser: true,
es2021: true,
},
extends: ['eslint:recommended', 'plugin:prettier/recommended'],
parserOptions: {
ecmaVersion: 12,
sourceType: 'module',
},
rules: {
'no-unused-vars': 'warn',
'no-console': 'off',
'prefer-const': 'error',
'eqeqeq': 'error'
},
};
结合Prettier统一代码风格:
json复制// .prettierrc
{
"semi": true,
"singleQuote": true,
"printWidth": 80,
"tabWidth": 2,
"trailingComma": "es5"
}
11. 构建与部署优化
11.1 模块打包配置
Webpack基础配置(webpack.config.js):
javascript复制const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader'
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
optimization: {
splitChunks: {
chunks: 'all'
}
}
};
11.2 代码分割策略
动态导入实现按需加载:
javascript复制// 传统方式
import { heavyModule } from './heavyModule';
heavyModule.doWork();
// 动态导入
document.getElementById('loadModule').addEventListener('click', async () => {
const module = await import('./heavyModule');
module.doWork();
});
React中的懒加载:
javascript复制const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
function MyApp() {
return (
<div>
<Suspense fallback={<div>加载中...</div>}>
<HeavyComponent />
</Suspense>
</div>
);
}
11.3 部署最佳实践
静态资源缓存策略:
nginx复制location /static/ {
alias /path/to/static/files;
expires 1y;
add_header Cache-Control "public, immutable";
}
location / {
try_files $uri $uri/ /index.html;
}
版本化文件名配置(webpack):
javascript复制output: {
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].chunk.js',
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css',
chunkFilename: '[name].[contenthash].chunk.css',
}),
]
12. 前端监控与错误追踪
12.1 错误捕获机制
全局错误监听:
javascript复制// 捕获未处理的Promise拒绝
window.addEventListener('unhandledrejection', event => {
console.error('未处理的Promise拒绝:', event.reason);
// 发送到错误监控服务
logErrorToService(event.reason);
});
// 捕获全局JavaScript错误
window.addEventListener('error', event => {
console.error('全局错误:', event.error);
logErrorToService(event.error);
return true; // 阻止默认处理(显示在控制台)
});
// 捕获资源加载失败
window.addEventListener('error', event => {
if (event.target && event.target.tagName) {
console.error('资源加载失败:', event.target.src || event.target.href);
logResourceErrorToService({
tag: event.target.tagName,
url: event.target.src || event.target.href,
});
}
}, true); // 使用捕获阶段
12.2 性能指标采集
核心Web指标监控:
javascript复制// 使用web-vitals库
import { getCLS, getFID, getLCP } from 'web-vitals';
function sendToAnalytics(metric) {
const body = JSON.stringify(metric);
navigator.sendBeacon('/analytics', body);
}
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getLCP(sendToAnalytics);
自定义性能测量:
javascript复制// 测量函数执行时间
function measure(fn, context = window) {
const start = performance.now();
const result = fn.call(context);
const duration = performance.now() - start;
console.log(`执行时间: ${duration.toFixed(2)}ms`);
return result;
}
// 使用
measure(() => heavyCalculation());
12.3 用户行为分析
点击热图实现原理:
javascript复制document.addEventListener('click', event => {
const target = event.target;
const path = getPathTo(target); // 获取元素在DOM中的路径
logClickEvent({
x: event.clientX,
y: event.clientY,
path: path,
timestamp: Date.now(),
viewport: {
width: window.innerWidth,
height: window.innerHeight
}
});
});
function getPathTo(element) {
if (!element || element === document) return [];
const path = [];
let current = element;
while (current && current !== document.body) {
let selector = current.tagName.toLowerCase();
if (current.id) {
selector += `#${current.id}`;
} else if (current.className) {
selector += `.${current.className.split(' ').join('.')}`;
}
path.unshift(selector);
current = current.parentNode;
}
return path;
}
13. JavaScript设计模式实战
13.1 观察者模式实现
自定义事件系统:
javascript复制class EventEmitter {
constructor() {
this.events = {};
}
on(event, listener) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(listener);
return this;
}
off(event, listener) {
if (!this.events[event]) return;
const index = this.events[event].indexOf(listener);
if (index > -1) {
this.events[event].splice(index, 1);
}
return this;
}
emit(event, ...args) {
if (!this.events[event]) return;
const listeners = this.events[event].slice();
for (const listener of listeners) {
listener.apply(this, args);
}
return this;
}
once(event, listener) {
const onceWrapper = (...args) => {
this.off(event, onceWrapper);
listener.apply(this, args);
};
return this.on(event, onceWrapper);
}
}
// 使用示例
const emitter = new EventEmitter();
emitter.on('data', data => console.log('收到数据:', data));
emitter.emit('data', { id: 1 }); // 输出: 收到数据: {id: 1}
13.2 策略模式应用
表单验证策略:
javascript复制// 验证策略
const strategies = {
isNonEmpty(value, errMsg) {
if (value.trim() === '') return errMsg;
},
minLength(value, length, errMsg) {
if (value.length < length) return errMsg;
},
isEmail(value, errMsg) {
if (!/^[^@]+@\w+(\.\w+)+\w$/.test(value)) return errMsg;
}
};
// 验证器
class Validator {
constructor() {
this.cache = [];
}
add(value, rules) {
for (const rule of rules) {
const strategyArr = rule.strategy.split(':');
const errMsg = rule.errMsg;
this.cache.push(() => {
const strategy = strategyArr.shift();
strategyArr.unshift(value);
strategyArr.push(errMsg);
return strategies[strategy].apply(null, strategyArr);
});
}
}
validate() {
for (const validatorFunc of this.cache) {
const errMsg = validatorFunc();
if (errMsg) return errMsg;
}
}
}
// 使用示例
const validateForm = () => {
const validator = new Validator();
validator.add(form.username.value, [
{ strategy: 'isNonEmpty', errMsg: '用户名不能为空' },
{ strategy: 'minLength:6', errMsg: '用户名长度不能少于6位' }
]);
validator.add(form.email.value, [
{ strategy: 'isNonEmpty', errMsg: '邮箱不能为空' },
{ strategy: 'isEmail', errMsg: '邮箱格式不正确' }
]);
return validator.validate();
};
13.3 装饰器模式应用
函数增强装饰器:
javascript复制// 简单的装饰器函数
function logDecorator(fn) {
return function(...args) {
console.log(`调用 ${fn.name} 参数:`, args);
const result = fn.apply(this, args);
console.log(`结果:`, result);
return result;
};
}
// 使用
function calculate(a, b) {
return a + b;
}
const decoratedCalc = logDecorator(calculate);
decoratedCalc(2, 3);
// 输出:
// 调用 calculate 参数: [2, 3]
// 结果: 5
// ES7装饰器语法(需要Babel转译)
class MathUtils {
@logDecorator
static multiply(a, b) {
return a * b;
}
}
MathUtils.multiply(4, 5);
// 输出:
// 调用 multiply 参数: [4, 5]
// 结果: 20
14. 前端工程化实践
14.1 Git工作流规范
功能分支工作流示例:
bash复制# 开始新功能
git checkout -b feature/new-authentication
# 开发过程中定期提交
git add .
git commit -m "实现JWT验证基础逻辑"
# 完成功能后合并到develop分支
git checkout develop
git pull origin develop
git merge --no-ff feature/new-authentication
git push origin develop
# 删除本地功能分支
git branch -d feature/new-authentication
提交消息规范(遵循Angular规范):
code复制<type>(<scope>): <subject>
<空行>
<body>
<空行>
<footer>
类型(type)包括:
- feat: 新功能
- fix: bug修复
- docs: 文档变更
- style: 代码格式变更
- refactor: 重构代码
- test: 测试相关
- chore: 构建过程或辅助工具的变动
14.2 CI/CD流水线配置
GitHub Actions示例(.github/workflows/build.yml):
yaml复制name: CI/CD Pipeline
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v2
with:
node-version: '14'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build project
run: npm run build
- name: Deploy to production
if: github.ref == 'refs/heads/main'
run: npm run deploy
14.3 文档自动化
JSDoc注释示例:
javascript复制/**
* 计算两个数字的和
* @param {number} a - 第一个加数
* @param {number} b - 第二个加数
* @returns {number} 两个参数的和
* @throws {TypeError} 当参数不是数字时抛出
* @example
* // returns 5
* add(2, 3);
*/
function add(a, b) {
if (typeof a !== 'number' || typeof b !== 'number') {
throw new TypeError('参数必须是数字');
}
return a + b;
}
生成文档命令(package.json):
json复制{
"scripts": {
"docs": "jsdoc -c jsdoc.json",
"serve-docs": "http-server docs -o"
}
}
15. 前端架构设计
15.1 状态管理方案对比
Redux核心概念实现:
javascript复制// 简化版Redux实现
function createStore(reducer) {
let state;
const listeners = [];
const getState = () => state;
const dispatch = (action) => {
state = reducer(state, action);
listeners.forEach(listener => listener());
};
const subscribe = (listener) => {
listeners.push(listener);
return () => {
const index = listeners.indexOf(listener);
listeners.splice(index, 1);
};
};
// 初始化state
dispatch({ type: '@@INIT' });
return { getState, dispatch, subscribe };
}
// 使用示例
function counterReducer(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
const store = createStore(counterReducer);
store.subscribe(() => console.log('状态更新:', store.getState()));
store.dispatch({ type: 'INCREMENT' }); // 输出: 状态更新: 1
15.2 组件设计原则
SOLID原则在前端的应用:
- 单一职责原则(SRP):
- 每个组件只做一件事
- 将大型组件拆分为多个小组件
