JavaScript代码的执行效率直接影响应用的性能。本章将深入探讨JavaScript代码层面的优化技术,包括算法优化、内存管理、V8引擎优化等。
const algorithmOptimization = {
bad: {
description: 'O(n²) 嵌套循环',
code: `
function findDuplicates(arr) {
const duplicates = [];
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j] && !duplicates.includes(arr[i])) {
duplicates.push(arr[i]);
}
}
}
return duplicates;
}
`,
complexity: 'O(n²) 或更差'
},
good: {
description: 'O(n) 使用Map',
code: `
function findDuplicates(arr) {
const seen = new Map();
const duplicates = new Set();
for (const item of arr) {
if (seen.has(item)) {
duplicates.add(item);
} else {
seen.set(item, true);
}
}
return [...duplicates];
}
`,
complexity: 'O(n)'
}
};
const loopOptimization = {
cacheLength: `
function processArray(arr) {
const len = arr.length;
for (let i = 0; i < len; i++) {
arr[i] = arr[i] * 2;
}
}
`,
reverseLoop: `
function processArrayReverse(arr) {
let i = arr.length;
while (i--) {
arr[i] = arr[i] * 2;
}
}
`,
forOf: `
function processArrayForOf(arr) {
for (const item of arr) {
console.log(item);
}
}
`,
avoidInLoop: `
const arr = [1, 2, 3, 4, 5];
for (let i = 0; i < arr.length; i++) {
if (arr[i] in obj) {
}
}
const hasOwn = Object.prototype.hasOwnProperty;
for (let i = 0; i < arr.length; i++) {
if (hasOwn.call(obj, arr[i])) {
}
}
`
};
const conditionOptimization = {
switchVsObject: `
function getStatusText(status) {
switch (status) {
case 0: return '待处理';
case 1: return '处理中';
case 2: return '已完成';
case 3: return '已取消';
default: return '未知';
}
}
const statusMap = {
0: '待处理',
1: '处理中',
2: '已完成',
3: '已取消'
};
function getStatusTextOptimized(status) {
return statusMap[status] ?? '未知';
}
`,
earlyReturn: `
function validate(user) {
if (!user) {
return { valid: false, error: '用户不存在' };
}
if (!user.name) {
return { valid: false, error: '姓名不能为空' };
}
if (!user.email) {
return { valid: false, error: '邮箱不能为空' };
}
if (!user.age || user.age < 0) {
return { valid: false, error: '年龄无效' };
}
return { valid: true };
}
`,
guardClauses: `
function processPayment(amount, user) {
if (!user.isAuthenticated) {
throw new Error('用户未认证');
}
if (amount <= 0) {
throw new Error('金额必须大于0');
}
if (!user.hasPaymentMethod) {
throw new Error('未添加支付方式');
}
return processTransaction(amount, user);
}
`
};
class Vector {
constructor(x = 0, y = 0, z = 0) {
this.x = x;
this.y = y;
this.z = z;
}
add(v) {
this.x += v.x;
this.y += v.y;
this.z += v.z;
return this;
}
clone() {
return new Vector(this.x, this.y, this.z);
}
}
const v1 = new Vector(1, 2, 3);
const v2 = new Vector(4, 5, 6);
v1.add(v2);
function createVector(x, y, z) {
return { x, y, z };
}
function addVectors(a, b, out) {
out.x = a.x + b.x;
out.y = a.y + b.y;
out.z = a.z + b.z;
return out;
}
const temp = createVector(0, 0, 0);
addVectors(v1, v2, temp);
class EventManager {
constructor() {
this.events = new Map();
this.boundHandlers = new WeakMap();
}
on(element, event, handler) {
const boundHandler = handler.bind(this);
this.boundHandlers.set(handler, boundHandler);
element.addEventListener(event, boundHandler);
if (!this.events.has(element)) {
this.events.set(element, new Map());
}
this.events.get(element).set(event, boundHandler);
}
off(element, event, handler) {
const boundHandler = this.boundHandlers.get(handler);
if (boundHandler) {
element.removeEventListener(event, boundHandler);
this.events.get(element)?.delete(event);
}
}
destroy() {
for (const [element, events] of this.events) {
for (const [event, handler] of events) {
element.removeEventListener(event, handler);
}
}
this.events.clear();
}
}
class Component {
constructor() {
this.data = [];
this.timer = null;
this.eventManager = new EventManager();
}
start() {
this.timer = setInterval(() => {
this.update();
}, 1000);
}
update() {
this.data.push(Date.now());
if (this.data.length > 100) {
this.data = this.data.slice(-50);
}
}
destroy() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
this.eventManager.destroy();
this.data = [];
}
}
const privateData = new WeakMap();
class SecretHolder {
constructor(secret) {
privateData.set(this, { secret });
}
getSecret() {
return privateData.get(this)?.secret;
}
setSecret(secret) {
privateData.get(this).secret = secret;
}
}
const holder = new SecretHolder('my secret');
console.log(holder.getSecret());
const cache = new WeakMap();
function getCachedData(obj) {
if (cache.has(obj)) {
return cache.get(obj);
}
const data = expensiveComputation(obj);
cache.set(obj, data);
return data;
}
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
const p1 = new Point(1, 2);
const p2 = new Point(3, 4);
const p3 = new Point(5, 6);
p3.z = 7;
function createPoint(x, y) {
return { x, y };
}
const p4 = createPoint(1, 2);
const p5 = createPoint(3, 4);
function getX(point) {
return point.x;
}
const p1 = { x: 1, y: 2 };
const p2 = { x: 3, y: 4 };
for (let i = 0; i < 10000; i++) {
getX(p1);
getX(p2);
}
const p3 = { x: 5, y: 6, z: 7 };
getX(p3);
const v8Optimization = {
monomorphic: `
function process(obj) {
return obj.value;
}
const obj = { value: 1 };
for (let i = 0; i < 10000; i++) {
process(obj);
}
`,
polymorphic: `
function process(obj) {
return obj.value;
}
for (let i = 0; i < 10000; i++) {
const obj = i % 2 === 0
? { value: 1 }
: { value: 2, extra: true };
process(obj);
}
`,
megamorphic: `
function process(obj) {
return obj.value;
}
for (let i = 0; i < 10000; i++) {
const obj = { value: i };
for (let j = 0; j < i % 10; j++) {
obj['prop' + j] = j;
}
process(obj);
}
`
};
const optimizationTraps = {
avoidTryCatch: `
function withTryCatch(arr) {
const result = [];
for (let i = 0; i < arr.length; i++) {
try {
result.push(arr[i] * 2);
} catch (e) {
result.push(0);
}
}
return result;
}
function withoutTryCatch(arr) {
const result = [];
for (let i = 0; i < arr.length; i++) {
result.push(arr[i] * 2);
}
return result;
}
`,
avoidArguments: `
function bad(a, b) {
arguments[0] = 10;
return a + b;
}
function good(a, b) {
'use strict';
return a + b;
}
`,
avoidDelete: `
const obj = { a: 1, b: 2, c: 3 };
delete obj.b;
const obj2 = { a: 1, b: 2, c: 3 };
obj2.b = undefined;
`
};
function square(x) {
return x * x;
}
function sumOfSquares(a, b) {
return square(a) + square(b);
}
function sumOfSquaresInlined(a, b) {
return a * a + b * b;
}
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
}
return (...moreArgs) => curried.apply(this, [...args, ...moreArgs]);
};
}
function add(a, b, c) {
return a + b + c;
}
const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3));
console.log(curriedAdd(1, 2)(3));
console.log(curriedAdd(1)(2, 3));
function partial(fn, ...presetArgs) {
return (...laterArgs) => fn(...presetArgs, ...laterArgs);
}
const add5 = partial(add, 2, 3);
console.log(add5(10));
function memoize(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
const fibonacci = memoize(function(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
});
console.log(fibonacci(50));
function memoizeWithLimit(fn, limit = 100) {
const cache = new Map();
const keys = [];
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn.apply(this, args);
cache.set(key, result);
keys.push(key);
if (keys.length > limit) {
const oldestKey = keys.shift();
cache.delete(oldestKey);
}
return result;
};
}
const dataStructureChoice = {
array: {
use: '有序数据、需要索引访问',
operations: {
access: 'O(1)',
search: 'O(n)',
insert: 'O(n)',
delete: 'O(n)'
}
},
object: {
use: '键值对存储、属性访问',
operations: {
access: 'O(1)',
search: 'O(n)',
insert: 'O(1)',
delete: 'O(1)'
}
},
map: {
use: '需要非字符串键、频繁增删',
operations: {
access: 'O(1)',
search: 'O(1)',
insert: 'O(1)',
delete: 'O(1)'
}
},
set: {
use: '唯一值集合、快速查找',
operations: {
has: 'O(1)',
add: 'O(1)',
delete: 'O(1)'
}
}
};
function benchmark() {
const size = 100000;
const arr = Array.from({ length: size }, (_, i) => i);
const set = new Set(arr);
const obj = Object.fromEntries(arr.map(i => [i, i]));
const map = new Map(arr.map(i => [i, i]));
const target = size - 1;
console.time('Array.includes');
arr.includes(target);
console.timeEnd('Array.includes');
console.time('Set.has');
set.has(target);
console.timeEnd('Set.has');
console.time('Object.hasOwn');
Object.hasOwn(obj, target);
console.timeEnd('Object.hasOwn');
console.time('Map.has');
map.has(target);
console.timeEnd('Map.has');
}
const arrayOptimization = {
pushVsConcat: `
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
arr1.push(...arr2);
const arr3 = arr1.concat(arr2);
`,
filterMapChain: `
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const result1 = data
.filter(x => x % 2 === 0)
.map(x => x * 2);
const result2 = data.reduce((acc, x) => {
if (x % 2 === 0) {
acc.push(x * 2);
}
return acc;
}, []);
`,
flatMap: `
const nested = [[1, 2], [3, 4], [5, 6]];
const result1 = nested.flat().map(x => x * 2);
const result2 = nested.flatMap(arr => arr.map(x => x * 2));
`,
spreadVsSlice: `
const arr = [1, 2, 3, 4, 5];
const copy1 = [...arr];
const copy2 = arr.slice();
`
};
async function parallelExecution() {
const urls = ['/api/1', '/api/2', '/api/3', '/api/4', '/api/5'];
console.time('串行');
const results1 = [];
for (const url of urls) {
results1.push(await fetch(url).then(r => r.json()));
}
console.timeEnd('串行');
console.time('并行');
const results2 = await Promise.all(
urls.map(url => fetch(url).then(r => r.json()))
);
console.timeEnd('并行');
}
async function parallelWithLimit(tasks, limit = 3) {
const results = [];
const executing = new Set();
for (const task of tasks) {
const promise = Promise.resolve().then(() => task());
results.push(promise);
executing.add(promise);
const cleanup = () => executing.delete(promise);
promise.then(cleanup, cleanup);
if (executing.size >= limit) {
await Promise.race(executing);
}
}
return Promise.all(results);
}
class TaskScheduler {
constructor(concurrency = 4) {
this.concurrency = concurrency;
this.running = 0;
this.queue = [];
}
async run(task) {
return new Promise((resolve, reject) => {
const execute = async () => {
this.running++;
try {
const result = await task();
resolve(result);
} catch (error) {
reject(error);
} finally {
this.running--;
this.next();
}
};
if (this.running < this.concurrency) {
execute();
} else {
this.queue.push(execute);
}
});
}
next() {
if (this.queue.length > 0 && this.running < this.concurrency) {
const next = this.queue.shift();
next();
}
}
}
const scheduler = new TaskScheduler(3);
const tasks = Array.from({ length: 10 }, (_, i) =>
() => new Promise(resolve => {
console.log(`任务 ${i} 开始`);
setTimeout(() => {
console.log(`任务 ${i} 完成`);
resolve(i);
}, 1000);
})
);
tasks.forEach(task => scheduler.run(task));
function measurePerformance() {
console.time('数据处理');
const data = Array.from({ length: 1000000 }, (_, i) => i);
const result = data.filter(x => x % 2 === 0).map(x => x * 2);
console.timeEnd('数据处理');
console.time('优化版本');
const result2 = [];
for (let i = 0; i < 1000000; i++) {
if (i % 2 === 0) {
result2.push(i * 2);
}
}
console.timeEnd('优化版本');
}
class Profiler {
constructor() {
this.profiles = new Map();
}
start(name) {
this.profiles.set(name, {
startTime: performance.now(),
startMemory: performance.memory?.usedJSHeapSize
});
}
end(name) {
const profile = this.profiles.get(name);
if (!profile) return null;
const endTime = performance.now();
const endMemory = performance.memory?.usedJSHeapSize;
const result = {
duration: endTime - profile.startTime,
memoryDelta: endMemory && profile.startMemory
? endMemory - profile.startMemory
: null
};
this.profiles.delete(name);
return result;
}
async profile(name, fn) {
this.start(name);
const result = await fn();
const stats = this.end(name);
console.log(`[${name}] 耗时: ${stats.duration.toFixed(2)}ms`);
if (stats.memoryDelta) {
console.log(`[${name}] 内存变化: ${(stats.memoryDelta / 1024).toFixed(2)}KB`);
}
return { result, stats };
}
}
const profiler = new Profiler();
profiler.profile('数组操作', () => {
const arr = Array.from({ length: 1000000 }, (_, i) => i);
return arr.filter(x => x % 2 === 0);
});
🚀 V8优化要点
- 保持对象结构一致:相同构造函数创建的对象应有相同属性
- 避免在热代码中使用try-catch:会阻止优化
- 使用monomorphic操作:保持函数参数类型一致
- 合理使用内置方法:V8对内置方法有深度优化
💡 性能优化检查清单
- 是否选择了正确的数据结构?
- 循环中是否有不必要的计算?
- 是否避免了内存泄漏?
- 异步操作是否并行执行?
- 是否有性能监控机制?
下一章将探讨 创建型模式,学习设计模式在JavaScript中的应用,从创建对象的各种模式开始。