1. JavaScript算法与手写函数实战指南
作为前端开发者,掌握JavaScript核心算法和手写常见函数是基本功。这些技能不仅能帮助你在面试中脱颖而出,更能提升日常开发效率。本文将深入讲解12类常见算法和函数的实现原理,并分享我在实际项目中的优化经验。
2. 排序算法实现与优化
2.1 快速排序的工程实践
快速排序的平均时间复杂度为O(n log n),是最常用的排序算法之一。以下是经过生产环境验证的实现:
javascript复制function quickSort(arr, left = 0, right = arr.length - 1) {
if (left >= right) return;
const pivot = partition(arr, left, right);
quickSort(arr, left, pivot - 1);
quickSort(arr, pivot + 1, right);
return arr;
}
function partition(arr, left, right) {
const pivot = arr[right];
let i = left;
for (let j = left; j < right; j++) {
if (arr[j] < pivot) {
[arr[i], arr[j]] = [arr[j], arr[i]];
i++;
}
}
[arr[i], arr[right]] = [arr[right], arr[i]];
return i;
}
关键优化点:
- 使用原地排序减少内存占用
- 采用三数取中法选择基准值避免最坏情况
- 对小数组切换为插入排序提升性能
2.2 冒泡排序的性能陷阱
虽然冒泡排序简单易懂,但其O(n²)的时间复杂度使其不适合大数据量排序。实际项目中仅在以下情况考虑使用:
javascript复制function bubbleSort(arr) {
let swapped;
do {
swapped = false;
for (let i = 0; i < arr.length - 1; i++) {
if (arr[i] > arr[i + 1]) {
[arr[i], arr[i + 1]] = [arr[i + 1], arr[i]];
swapped = true;
}
}
} while (swapped);
return arr;
}
注意事项:
- 当数组长度超过1000时性能急剧下降
- 已排序数组的最佳情况仍需O(n)时间
- 可用标志位优化减少不必要的比较
3. 数组操作实战技巧
3.1 数组去重的六种方案对比
| 方法 | 时间复杂度 | 特点 | 适用场景 |
|---|---|---|---|
| Set | O(n) | 最简单 | 基础去重 |
| filter+indexOf | O(n²) | 兼容性好 | 需要IE支持 |
| reduce | O(n²) | 函数式 | 链式操作 |
| Map | O(n) | 高性能 | 大数据量 |
| 双重循环 | O(n²) | 无需新API | 教学演示 |
| includes | O(n²) | 可读性强 | 小数组 |
生产环境推荐方案:
javascript复制// 兼顾性能和可读性
function unique(arr) {
const seen = new Map();
return arr.filter(item => {
return !seen.has(item) && seen.set(item, true);
});
}
3.2 reduce的进阶用法
reduce不仅能求和,还能实现复杂数据转换:
javascript复制// 按属性分组
const groupBy = (arr, key) => arr.reduce((acc, obj) => {
const group = obj[key];
acc[group] = acc[group] || [];
acc[group].push(obj);
return acc;
}, {});
// 复合数据转换
const processData = data => data.reduce((acc, item) => ({
sum: acc.sum + item.value,
max: Math.max(acc.max, item.value),
min: Math.min(acc.min, item.value),
avg: (acc.sum + item.value) / (acc.count + 1),
count: acc.count + 1
}), { sum: 0, max: -Infinity, min: Infinity, avg: 0, count: 0 });
4. 函数式编程实战
4.1 防抖与节流的本质区别
| 特性 | 防抖(debounce) | 节流(throttle) |
|---|---|---|
| 触发时机 | 停止操作后执行 | 固定间隔执行 |
| 执行次数 | 只执行最后一次 | 周期性执行 |
| 适用场景 | 搜索建议 | 滚动事件 |
生产级实现:
javascript复制// 防抖:带立即执行选项
function debounce(fn, delay, immediate = false) {
let timer;
return function(...args) {
const context = this;
const later = () => {
timer = null;
if (!immediate) fn.apply(context, args);
};
const callNow = immediate && !timer;
clearTimeout(timer);
timer = setTimeout(later, delay);
if (callNow) fn.apply(context, args);
};
}
// 节流:带尾调用选项
function throttle(fn, delay, trailing = true) {
let last = 0, timer;
return function(...args) {
const now = Date.now();
const context = this;
if (now - last >= delay) {
if (timer) {
clearTimeout(timer);
timer = null;
}
fn.apply(context, args);
last = now;
} else if (trailing && !timer) {
timer = setTimeout(() => {
fn.apply(context, args);
timer = null;
last = Date.now();
}, delay - (now - last));
}
};
}
4.2 柯里化的工程应用
柯里化在参数预设和函数组合中非常有用:
javascript复制// 自动柯里化工具函数
function curry(fn, arity = fn.length) {
return function curried(...args) {
if (args.length >= arity) return fn(...args);
return (...moreArgs) => curried(...args, ...moreArgs);
};
}
// 实际应用:创建验证器
const validate = curry((rules, value) => rules.every(rule => rule(value)));
const isString = x => typeof x === 'string';
const minLength = len => str => str.length >= len;
const validateUsername = validate([isString, minLength(6)]);
console.log(validateUsername('admin')); // false
console.log(validateUsername('username')); // true
5. 异步编程进阶
5.1 Promise.all的工业级实现
javascript复制Promise.myAll = function(promises) {
if (!Array.isArray(promises)) {
return Promise.reject(new TypeError('Arguments must be an array'));
}
return new Promise((resolve, reject) => {
const results = new Array(promises.length);
let remaining = promises.length;
if (remaining === 0) {
return resolve(results);
}
const onFulfilled = (index, value) => {
results[index] = value;
if (--remaining === 0) {
resolve(results);
}
};
promises.forEach((promise, index) => {
Promise.resolve(promise).then(
value => onFulfilled(index, value),
reject
);
});
});
};
关键改进:
- 增加参数类型检查
- 处理空数组情况
- 提前Promise.resolve包装
- 使用计数器替代数组检查
5.2 异步流程控制实战
javascript复制// 带并发控制的异步任务执行
async function parallelWithLimit(tasks, limit = 5) {
const results = [];
const executing = new Set();
for (const task of tasks) {
const p = Promise.resolve().then(() => task());
executing.add(p);
results.push(p);
p.then(() => executing.delete(p));
if (executing.size >= limit) {
await Promise.race(executing);
}
}
return Promise.all(results);
}
// 使用示例
const fetchUrls = urls => parallelWithLimit(
urls.map(url => () => fetch(url).then(r => r.json())),
3
);
6. 实用工具函数实现
6.1 深度对象克隆
javascript复制function deepClone(obj, cache = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (cache.has(obj)) return cache.get(obj);
const Constructor = obj.constructor;
let clone;
switch (Constructor) {
case RegExp:
clone = new Constructor(obj);
break;
case Date:
clone = new Constructor(obj.getTime());
break;
default:
clone = Array.isArray(obj) ? [] : Object.create(Object.getPrototypeOf(obj));
}
cache.set(obj, clone);
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key], cache);
}
}
return clone;
}
6.2 性能优化的sleep函数
javascript复制// 高精度sleep实现
async function sleep(ms) {
return new Promise(resolve => {
const start = performance.now();
const frame = (now) => {
if (now - start >= ms) {
resolve();
} else {
requestAnimationFrame(frame);
}
};
requestAnimationFrame(frame);
});
}
// 使用示例
async function animate() {
console.log('开始');
await sleep(1000); // 精确到帧率
console.log('1秒后');
}
7. 手写核心API实现
7.1 完整的new操作符模拟
javascript复制function myNew(Constructor, ...args) {
// 1. 创建新对象并链接原型
const instance = Object.create(Constructor.prototype);
// 2. 执行构造函数绑定this
const result = Constructor.apply(instance, args);
// 3. 处理构造函数返回值
return (typeof result === 'object' && result !== null) ||
typeof result === 'function'
? result
: instance;
}
// 测试用例
function Person(name) {
this.name = name;
// 返回原始值会被忽略
return 123;
}
const p = myNew(Person, 'Alice');
console.log(p instanceof Person); // true
console.log(p.name); // 'Alice'
7.2 完整的Promise实现
javascript复制class MyPromise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
const reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
const promise2 = new MyPromise((resolve, reject) => {
const handleFulfilled = () => {
queueMicrotask(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (err) {
reject(err);
}
});
};
const handleRejected = () => {
queueMicrotask(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (err) {
reject(err);
}
});
};
if (this.state === 'fulfilled') {
handleFulfilled();
} else if (this.state === 'rejected') {
handleRejected();
} else {
this.onFulfilledCallbacks.push(handleFulfilled);
this.onRejectedCallbacks.push(handleRejected);
}
});
return promise2;
}
catch(onRejected) {
return this.then(null, onRejected);
}
static resolve(value) {
return new MyPromise(resolve => resolve(value));
}
static reject(reason) {
return new MyPromise((_, reject) => reject(reason));
}
}
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected'));
}
if (x instanceof MyPromise) {
x.then(resolve, reject);
} else {
resolve(x);
}
}
8. 性能优化与调试技巧
8.1 算法性能测试方法
javascript复制function measurePerformance(fn, ...args) {
const start = performance.now();
const result = fn(...args);
const end = performance.now();
console.log(`执行时间: ${(end - start).toFixed(2)}ms`);
console.log('返回结果:', result);
return result;
}
// 使用示例
measurePerformance(quickSort, Array.from({length: 10000}, () => Math.random()));
8.2 内存泄漏检测
javascript复制// 在Node.js中检测内存泄漏
const { performance, PerformanceObserver } = require('perf_hooks');
const obs = new PerformanceObserver((list) => {
const entry = list.getEntries()[0];
console.log(`内存使用: ${entry.details.memory.usedJSHeapSize / 1024 / 1024} MB`);
});
obs.observe({ entryTypes: ['gc'], buffered: true });
// 在浏览器中使用Performance API
function checkMemory() {
if (window.performance && window.performance.memory) {
console.log(
`已用内存: ${(performance.memory.usedJSHeapSize / 1048576).toFixed(2)} MB /
总内存: ${(performance.memory.totalJSHeapSize / 1048576).toFixed(2)} MB`
);
}
}
9. 浏览器API模拟实现
9.1 完整的EventEmitter实现
javascript复制class EventEmitter {
constructor() {
this.events = {};
}
on(event, listener) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(listener);
return this;
}
once(event, listener) {
const onceWrapper = (...args) => {
this.off(event, onceWrapper);
listener.apply(this, args);
};
return this.on(event, onceWrapper);
}
off(event, listener) {
if (!this.events[event]) return this;
if (!listener) {
delete this.events[event];
return this;
}
this.events[event] = this.events[event].filter(l => l !== listener);
return this;
}
emit(event, ...args) {
if (!this.events[event]) return false;
const listeners = this.events[event].slice();
listeners.forEach(listener => listener.apply(this, args));
return true;
}
listenerCount(event) {
return this.events[event] ? this.events[event].length : 0;
}
}
9.2 Fetch API的简化实现
javascript复制function myFetch(url, options = {}) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(options.method || 'GET', url);
if (options.headers) {
Object.entries(options.headers).forEach(([key, value]) => {
xhr.setRequestHeader(key, value);
});
}
xhr.onload = () => {
const response = {
ok: xhr.status >= 200 && xhr.status < 300,
status: xhr.status,
statusText: xhr.statusText,
url: xhr.responseURL,
headers: xhr.getAllResponseHeaders().split('\r\n').reduce((acc, line) => {
if (line) {
const [key, value] = line.split(': ');
acc[key] = value;
}
return acc;
}, {}),
text: () => Promise.resolve(xhr.responseText),
json: () => Promise.resolve(JSON.parse(xhr.responseText)),
blob: () => Promise.resolve(new Blob([xhr.response]))
};
resolve(response);
};
xhr.onerror = () => reject(new TypeError('Network request failed'));
xhr.ontimeout = () => reject(new TypeError('Request timeout'));
xhr.send(options.body);
});
}
10. 数据结构实现
10.1 链表实现
javascript复制class ListNode {
constructor(value, next = null) {
this.value = value;
this.next = next;
}
}
class LinkedList {
constructor() {
this.head = null;
this.tail = null;
this.length = 0;
}
append(value) {
const newNode = new ListNode(value);
if (!this.head) {
this.head = newNode;
this.tail = newNode;
} else {
this.tail.next = newNode;
this.tail = newNode;
}
this.length++;
return this;
}
prepend(value) {
const newNode = new ListNode(value, this.head);
this.head = newNode;
if (!this.tail) {
this.tail = newNode;
}
this.length++;
return this;
}
insert(index, value) {
if (index >= this.length) return this.append(value);
if (index <= 0) return this.prepend(value);
const leader = this.traverseToIndex(index - 1);
const newNode = new ListNode(value, leader.next);
leader.next = newNode;
this.length++;
return this;
}
remove(index) {
if (index < 0 || index >= this.length) return this;
if (index === 0) {
this.head = this.head.next;
if (!this.head) this.tail = null;
} else {
const leader = this.traverseToIndex(index - 1);
leader.next = leader.next.next;
if (index === this.length - 1) {
this.tail = leader;
}
}
this.length--;
return this;
}
traverseToIndex(index) {
let counter = 0;
let currentNode = this.head;
while (counter !== index) {
currentNode = currentNode.next;
counter++;
}
return currentNode;
}
reverse() {
if (!this.head.next) return this;
let first = this.head;
this.tail = this.head;
let second = first.next;
while (second) {
const temp = second.next;
second.next = first;
first = second;
second = temp;
}
this.head.next = null;
this.head = first;
return this;
}
}
10.2 哈希表实现
javascript复制class HashTable {
constructor(size = 53) {
this.keyMap = new Array(size);
}
_hash(key) {
let total = 0;
const WEIRD_PRIME = 31;
for (let i = 0; i < Math.min(key.length, 100); i++) {
const char = key[i];
const value = char.charCodeAt(0) - 96;
total = (total * WEIRD_PRIME + value) % this.keyMap.length;
}
return total;
}
set(key, value) {
const index = this._hash(key);
if (!this.keyMap[index]) {
this.keyMap[index] = [];
}
// 检查是否已存在相同key
for (let i = 0; i < this.keyMap[index].length; i++) {
if (this.keyMap[index][i][0] === key) {
this.keyMap[index][i][1] = value;
return;
}
}
this.keyMap[index].push([key, value]);
}
get(key) {
const index = this._hash(key);
if (this.keyMap[index]) {
for (const [k, v] of this.keyMap[index]) {
if (k === key) return v;
}
}
return undefined;
}
keys() {
const keysArr = [];
for (const bucket of this.keyMap) {
if (bucket) {
for (const [k] of bucket) {
if (!keysArr.includes(k)) {
keysArr.push(k);
}
}
}
}
return keysArr;
}
values() {
const valuesArr = [];
for (const bucket of this.keyMap) {
if (bucket) {
for (const [_, v] of bucket) {
if (!valuesArr.includes(v)) {
valuesArr.push(v);
}
}
}
}
return valuesArr;
}
}
11. 设计模式实现
11.1 观察者模式实现
javascript复制class Observable {
constructor() {
this.observers = new Set();
}
subscribe(observer) {
this.observers.add(observer);
return {
unsubscribe: () => this.observers.delete(observer)
};
}
notify(data) {
this.observers.forEach(observer => observer(data));
}
}
// 使用示例
const observable = new Observable();
const subscription1 = observable.subscribe(data => {
console.log('Observer 1 received:', data);
});
const subscription2 = observable.subscribe(data => {
console.log('Observer 2 received:', data);
});
observable.notify('Hello observers!');
subscription1.unsubscribe();
observable.notify('Second message');
11.2 单例模式实现
javascript复制const Singleton = (function() {
let instance;
function createInstance() {
const object = new Object('I am the instance');
return object;
}
return {
getInstance: function() {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
// 使用示例
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // true
12. 前端工程化工具
12.1 简易打包工具实现
javascript复制const fs = require('fs');
const path = require('path');
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const { transformFromAst } = require('@babel/core');
function createAsset(filename) {
const content = fs.readFileSync(filename, 'utf-8');
const ast = parser.parse(content, {
sourceType: 'module'
});
const dependencies = [];
traverse(ast, {
ImportDeclaration: ({ node }) => {
dependencies.push(node.source.value);
}
});
const { code } = transformFromAst(ast, null, {
presets: ['@babel/preset-env']
});
return {
filename,
dependencies,
code
};
}
function createGraph(entry) {
const mainAsset = createAsset(entry);
const queue = [mainAsset];
for (const asset of queue) {
asset.mapping = {};
const dirname = path.dirname(asset.filename);
asset.dependencies.forEach(relativePath => {
const absolutePath = path.join(dirname, relativePath);
const child = createAsset(absolutePath);
asset.mapping[relativePath] = child.filename;
queue.push(child);
});
}
return queue;
}
function bundle(graph) {
let modules = '';
graph.forEach(mod => {
modules += `'${mod.filename}': [
function(require, module, exports) {
${mod.code}
},
${JSON.stringify(mod.mapping)}
],`;
});
const result = `
(function(modules) {
function require(id) {
const [fn, mapping] = modules[id];
function localRequire(relativePath) {
return require(mapping[relativePath]);
}
const module = { exports: {} };
fn(localRequire, module, module.exports);
return module.exports;
}
require('${graph[0].filename}');
})({${modules}})
`;
return result;
}
// 使用示例
const graph = createGraph('./example/entry.js');
const result = bundle(graph);
fs.writeFileSync('./bundle.js', result);
12.2 热更新实现原理
javascript复制class HotModuleReplacement {
constructor() {
this.modules = new Map();
this.hotCallbacks = new Map();
this.socket = new WebSocket('ws://localhost:8080');
this.socket.onmessage = (event) => {
const { type, data } = JSON.parse(event.data);
if (type === 'update') {
this.handleUpdate(data);
}
};
}
register(moduleId, hot) {
this.modules.set(moduleId, hot);
if (hot.data) {
hot.data = {};
}
const callbacks = [];
this.hotCallbacks.set(moduleId, callbacks);
return {
accept: (deps, callback) => {
if (typeof deps === 'function') {
callback = deps;
deps = [];
}
callbacks.push({ deps, callback });
},
dispose: (callback) => {
hot.dispose = callback;
},
data: hot.data || {}
};
}
handleUpdate(updatedModules) {
updatedModules.forEach(moduleId => {
const hot = this.modules.get(moduleId);
if (hot && hot.dispose) {
hot.dispose();
}
// 重新执行模块代码
System.import(`${moduleId}?t=${Date.now()}`).then(newModule => {
const callbacks = this.hotCallbacks.get(moduleId) || [];
callbacks.forEach(({ callback }) => {
if (typeof callback === 'function') {
callback();
}
});
});
});
}
}
// 在模块中使用
const hot = new HotModuleReplacement();
const moduleId = './myModule.js';
if (module.hot) {
module.hot.accept([moduleId], () => {
// 模块更新后的处理逻辑
console.log('模块已热更新');
});
}
13. 测试与调试技巧
13.1 单元测试框架实现
javascript复制class TestFramework {
constructor() {
this.tests = [];
this.beforeEachFns = [];
this.afterEachFns = [];
}
describe(description, fn) {
console.log(`\n${description}`);
fn();
}
it(description, fn) {
this.tests.push({ description, fn });
try {
this.runBeforeEach();
fn();
this.runAfterEach();
console.log(` ✓ ${description}`);
} catch (err) {
this.runAfterEach();
console.error(` ✗ ${description}`);
console.error(` ${err.message}`);
}
}
beforeEach(fn) {
this.beforeEachFns.push(fn);
}
afterEach(fn) {
this.afterEachFns.push(fn);
}
runBeforeEach() {
this.beforeEachFns.forEach(fn => fn());
}
runAfterEach() {
this.afterEachFns.forEach(fn => fn());
}
expect(actual) {
return {
toBe(expected) {
if (actual !== expected) {
throw new Error(`Expected ${actual} to be ${expected}`);
}
},
toEqual(expected) {
if (JSON.stringify(actual) !== JSON.stringify(expected)) {
throw new Error(`Expected ${JSON.stringify(actual)} to equal ${JSON.stringify(expected)}`);
}
},
toThrow(expected) {
let threw = false;
try {
actual();
} catch (err) {
threw = true;
if (expected && err.message !== expected) {
throw new Error(`Expected error message "${expected}" but got "${err.message}"`);
}
}
if (!threw) {
throw new Error('Expected function to throw an error');
}
}
};
}
}
// 使用示例
const test = new TestFramework();
test.describe('Array', () => {
let arr;
test.beforeEach(() => {
arr = [1, 2, 3];
});
test.it('should have length 3', () => {
test.expect(arr.length).toBe(3);
});
test.it('should contain value 2', () => {
test.expect(arr.indexOf(2)).not.toBe(-1);
});
});
13.2 性能分析工具
javascript复制class Profiler {
constructor() {
this.marks = new Map();
this.measurements = [];
}
mark(name) {
this.marks.set(name, performance.now());
}
measure(startMark, endMark, name) {
const start = this.marks.get(startMark);
const end = this.marks.get(endMark);
if (start === undefined || end === undefined) {
throw new Error('Mark not found');
}
this.measurements.push({
name: name || `${startMark} -> ${endMark}`,
duration: end - start
});
}
getMeasures() {
return this.measurements;
}
printMeasures() {
console.log('\nPerformance Measurements:');
this.measurements.forEach(({ name, duration }) => {
console.log(`${name}: ${duration.toFixed(2)}ms`);
});
}
static time(fn) {
const start = performance.now();
const result = fn();
const end = performance.now();
return {
result,
duration: end - start
};
}
}
// 使用示例
const profiler = new Profiler();
profiler.mark('start');
// 执行一些操作
for (let i = 0; i < 1000000; i++) Math.sqrt(i);
profiler.mark('end');
profiler.measure('start', 'end', 'sqrt calculation');
profiler.printMeasures();
// 简单计时
const { result, duration } = Profiler.time(() => {
return Array.from({ length: 1000 }, (_, i) => i * 2);
});
console.log(`Operation took ${duration.toFixed(2)}ms`);
14. 安全相关实现
14.1 XSS防护函数
javascript复制function sanitizeHTML(str) {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
function escapeHTML(unsafe) {
return unsafe.replace(/[&<>"']/g, match => {
switch (match) {
case '&': return '&';
case '<': return '<';
case '>': return '>';
case '"': return '"';
case "'": return ''';
default: return match;
}
});
}
function safeURL(url) {
try {
const parsed = new URL(url);
const allowedProtocols = ['http:', 'https:', 'mailto:', 'tel:'];
if (!allowedProtocols.includes(parsed.protocol)) {
return 'about:blank';
}
return url;
} catch {
return 'about:blank';
}
}
14.2 CSRF防护实现
javascript复制class CSRFProtection {
constructor() {
this.token = this.generateToken();
this.headerName = 'X-CSRF-Token';
}
generateToken() {
const array = new Uint8Array(32);
window.crypto.getRandomValues(array);
return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
}
middleware(req, res, next) {
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method)) {
const clientToken = req.headers[this.headerName.toLowerCase()] || req.body._csrf;
if (!clientToken || clientToken !== this.token) {
return res.status(403).send('CSRF token validation failed');
}
}
next();
}
getToken() {
return this.token;
}
injectToken(formHTML) {
return formHTML.replace(
/(<form[^>]*method=["'](post|put|patch|delete)["'][^>]*>)/i,
`$1<input type="hidden" name="_csrf" value="${this.token}">`
);
}
}
// 使用示例
const csrf = new CSRFProtection();
// Express中间件
app.use((req, res, next) => csrf.middleware(req, res, next));
// 在表单中注入token
const form = `
<form method="POST" action="/submit">
<input type="text" name="username">
<button type="submit">Submit</button>
</form>
`;
const protectedForm = csrf.injectToken(form);
15. 图形与动画实现
15.1 Canvas动画引擎
javascript复制class CanvasAnimation {
constructor(canvas, options = {}) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.animations = [];
this.lastTime = 0;
this.isRunning = false;
this.setOptions(options);
this.resizeCanvas();
window.addEventListener('resize', () => this.resizeCanvas());
}
setOptions(options) {
this.options = {
backgroundColor: '#f0f0f0',
width: window.innerWidth,
height: window.innerHeight,
...options
};
}
resizeCanvas() {
this.canvas.width = this.options.width;
this.canvas.height = this.options.height;
this.draw();
}
addAnimation(animation) {
this.animations.push(animation);
if (!this.isRunning) this.start();
}
removeAnimation(animation) {
const index = this.animations.indexOf(animation);
if (index !== -1) this.animations.splice(index, 1);
if (this.animations.length === 0) this.stop();
}
clear() {
this.ctx.fillStyle = this.options.backgroundColor;
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
}
draw(timestamp = 0) {
if (!this.isRunning) return;
const deltaTime = timestamp - this.lastTime;
this.lastTime = timestamp;
this.clear();
for (const animation of this.animations) {
animation.update(deltaTime);
animation.draw(this.ctx);
}
requestAnimationFrame(t => this.draw(t));
}
start() {