【JavaScript】性能优化与安全

在这里插入图片描述

个人主页:Guiat
归属专栏:HTML CSS JavaScript

在这里插入图片描述

正文

1. JavaScript 性能优化

1.1 代码优化

1.1.1 减少 DOM 操作

DOM 操作是昂贵的,应尽量减少:

// 低效: 多次单独操作 DOM
for (let i = 0; i < 1000; i++) {
  document.body.innerHTML += '<div>' + i + '</div>';
}

// 高效: 批量操作后一次性更新 DOM
let html = '';
for (let i = 0; i < 1000; i++) {
  html += '<div>' + i + '</div>';
}
document.body.innerHTML = html;

// 更高效: 使用文档片段
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
  const div = document.createElement('div');
  div.textContent = i;
  fragment.appendChild(div);
}
document.body.appendChild(fragment);

1.1.2 使用事件委托

利用事件冒泡,在父元素上处理子元素事件:

// 低效: 为每个按钮添加事件监听器
document.querySelectorAll('button').forEach(button => {
  button.addEventListener('click', function(e) {
    console.log('Button clicked:', this.textContent);
  });
});

// 高效: 使用事件委托
document.querySelector('.button-container').addEventListener('click', function(e) {
  if (e.target.tagName === 'BUTTON') {
    console.log('Button clicked:', e.target.textContent);
  }
});

1.1.3 避免全局变量

全局变量会降低变量查找速度并增加命名冲突风险:

// 不推荐: 全局变量
var count = 0;
function increment() {
  count++;
}

// 推荐: 使用模块或闭包
const counter = (function() {
  let count = 0;
  return {
    increment() { count++; },
    getCount() { return count; }
  };
})();

1.1.4 优化循环

const arr = [1, 2, 3, 4, 5];

// 低效: 每次迭代都计算数组长度
for (let i = 0; i < arr.length; i++) { /* ... */ }

// 高效: 缓存数组长度
for (let i = 0, len = arr.length; i < len; i++) { /* ... */ }

// 现代方法: 使用数组方法
arr.forEach(item => { /* ... */ });
// 或
for (const item of arr) { /* ... */ }

1.2 资源加载优化

1.2.1 脚本加载策略

<!-- 阻塞渲染的脚本 -->
<script src="script.js"></script>

<!-- 异步加载,不阻塞渲染,但加载完立即执行 -->
<script async src="script.js"></script>

<!-- 延迟加载,不阻塞渲染,在HTML解析完成后按顺序执行 -->
<script defer src="script.js"></script>

1.2.2 懒加载

// 图片懒加载示例
document.addEventListener('DOMContentLoaded', function() {
  const lazyImages = document.querySelectorAll('img.lazy');
  
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const img = entry.target;
        img.src = img.dataset.src;
        img.classList.remove('lazy');
        observer.unobserve(img);
      }
    });
  });
  
  lazyImages.forEach(img => observer.observe(img));
});

1.3 渲染优化

1.3.1 避免布局抖动(Layout Thrashing)

// 低效: 强制多次重排
const boxes = document.querySelectorAll('.box');
boxes.forEach(box => {
  const height = box.offsetHeight; // 读取
  box.style.height = (height * 2) + 'px'; // 写入
  const width = box.offsetWidth; // 读取
  box.style.width = (width * 2) + 'px'; // 写入
});

// 高效: 批量读取后批量写入
const boxes = document.querySelectorAll('.box');
// 读取阶段
const dimensions = Array.from(boxes).map(box => ({
  height: box.offsetHeight,
  width: box.offsetWidth
}));
// 写入阶段
boxes.forEach((box, i) => {
  box.style.height = (dimensions[i].height * 2) + 'px';
  box.style.width = (dimensions[i].width * 2) + 'px';
});

1.3.2 使用 requestAnimationFrame

// 低效: 使用setTimeout进行动画
function animate() {
  element.style.left = (parseFloat(element.style.left) || 0) + 1 + 'px';
  setTimeout(animate, 16); // 约60fps
}

// 高效: 使用requestAnimationFrame
function animate() {
  element.style.left = (parseFloat(element.style.left) || 0) + 1 + 'px';
  requestAnimationFrame(animate);
}
requestAnimationFrame(animate);

2. JavaScript 安全

2.1 XSS (跨站脚本攻击)

2.1.1 XSS 攻击示例

// 危险: 直接插入用户输入
const userInput = '<script>alert("XSS攻击!")</script>';
document.getElementById('output').innerHTML = userInput;

// 安全: 转义用户输入
function escapeHTML(str) {
  return str.replace(/[&<>"']/g, m => ({
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#39;'
  }[m]));
}
document.getElementById('output').innerHTML = escapeHTML(userInput);

2.1.2 防御 XSS

  1. 内容安全策略 (CSP)
<!-- 在HTML头部添加CSP -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
  1. 使用 textContent 而非 innerHTML
// 安全: 使用textContent
document.getElementById('output').textContent = userInput;
  1. 使用现代框架的自动转义功能
// React自动转义
function App() {
  const userInput = '<script>alert("XSS")</script>';
  return <div>{userInput}</div>; // React自动转义
}

2.2 CSRF (跨站请求伪造)

2.2.1 CSRF 攻击示例

攻击者可能在恶意网站上放置如下代码:

<img src="https://bank.example/transfer?to=attacker&amount=1000" style="display:none">

2.2.2 防御 CSRF

  1. 使用 CSRF Token
// 服务器生成token并发送给客户端
const csrfToken = generateRandomToken();
// 客户端发送请求时附带token
fetch('/api/transfer', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-CSRF-Token': csrfToken
  },
  body: JSON.stringify(data)
});
  1. 使用 SameSite Cookie 属性
// 服务器设置Cookie
document.cookie = "sessionId=abc123; SameSite=Strict";

2.3 输入验证与净化

// 客户端验证
function validateEmail(email) {
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return regex.test(email);
}

// 数据净化
function sanitizeInput(input) {
  // 移除所有HTML标签
  return input.replace(/<[^>]*>/g, '');
}

2.4 安全的 JSON 解析

// 危险: 使用eval解析JSON
const userData = eval('(' + jsonString + ')'); // 不安全!

// 安全: 使用JSON.parse
try {
  const userData = JSON.parse(jsonString);
} catch (e) {
  console.error('Invalid JSON');
}

2.5 避免原型污染

// 危险: 直接合并用户提供的对象
Object.assign(config, userProvidedConfig);

// 安全: 使用Object.create(null)创建无原型对象
const safeConfig = Object.create(null);
for (const key in userProvidedConfig) {
  if (Object.prototype.hasOwnProperty.call(userProvidedConfig, key) && key !== '__proto__') {
    safeConfig[key] = userProvidedConfig[key];
  }
}

2.6 使用 HTTPS

确保所有API请求和资源加载使用HTTPS:

// 强制重定向到HTTPS
if (location.protocol !== 'https:') {
  location.replace(`https:${location.href.substring(location.protocol.length)}`);
}

2.7 安全的本地存储

// 敏感数据不应存储在localStorage
// 不安全: 
localStorage.setItem('authToken', token);

// 更安全: 使用sessionStorage或加密后存储
sessionStorage.setItem('authToken', token);

// 或使用加密库加密后存储
const encryptedToken = encryptData(token, secretKey);
localStorage.setItem('authToken', encryptedToken);

3. 高级性能优化技术

3.1 Web Workers

将耗时计算移至后台线程:

// main.js
const worker = new Worker('worker.js');
worker.onmessage = function(e) {
  console.log('计算结果:', e.data);
};
worker.postMessage({numbers: [1, 2, 3, 4, 5]});

// worker.js
self.onmessage = function(e) {
  const numbers = e.data.numbers;
  const result = numbers.reduce((sum, num) => sum + num, 0);
  self.postMessage(result);
};

3.2 内存管理

// 避免内存泄漏
function createNodes() {
  const nodes = [];
  for (let i = 0; i < 10000; i++) {
    nodes.push(document.createElement('div'));
  }
  
  // 不好: 未清理引用
  // return nodes;
  
  // 好: 使用后清理引用
  const result = doSomethingWith(nodes);
  nodes.length = 0; // 清空数组
  return result;
}

3.3 使用缓存

// 函数结果缓存 (记忆化)
function memoize(fn) {
  const cache = {};
  return function(...args) {
    const key = JSON.stringify(args);
    if (!(key in cache)) {
      cache[key] = fn.apply(this, args);
    }
    return cache[key];
  };
}

const expensiveCalculation = memoize(function(n) {
  console.log('计算中...');
  return n * n;
});

console.log(expensiveCalculation(4)); // 输出: 计算中... 16
console.log(expensiveCalculation(4)); // 输出: 16 (使用缓存)

3.4 虚拟列表

处理大量数据时,只渲染可视区域内的元素:

class VirtualList {
  constructor(container, itemHeight, totalItems, renderItem) {
    this.container = container;
    this.itemHeight = itemHeight;
    this.totalItems = totalItems;
    this.renderItem = renderItem;
    
    this.visibleItems = Math.ceil(container.clientHeight / itemHeight) + 2;
    this.scrollTop = 0;
    this.startIndex = 0;
    
    this.init();
  }
  
  init() {
    this.container.style.position = 'relative';
    this.container.style.overflow = 'auto';
    this.container.style.height = '100%';
    
    const totalHeight = this.totalItems * this.itemHeight;
    this.content = document.createElement('div');
    this.content.style.height = `${totalHeight}px`;
    this.content.style.position = 'relative';
    this.container.appendChild(this.content);
    
    this.items = [];
    for (let i = 0; i < this.visibleItems; i++) {
      const item = document.createElement('div');
      item.style.position = 'absolute';
      item.style.width = '100%';
      item.style.height = `${this.itemHeight}px`;
      this.content.appendChild(item);
      this.items.push(item);
    }
    
    this.container.addEventListener('scroll', this.onScroll.bind(this));
    this.render();
  }
  
  onScroll() {
    this.scrollTop = this.container.scrollTop;
    this.render();
  }
  
  render() {
    this.startIndex = Math.floor(this.scrollTop / this.itemHeight);
    
    for (let i = 0; i < this.visibleItems; i++) {
      const index = this.startIndex + i;
      const item = this.items[i];
      
      if (index < this.totalItems) {
        item.style.top = `${index * this.itemHeight}px`;
        item.style.display = 'block';
        this.renderItem(item, index);
      } else {
        item.style.display = 'none';
      }
    }
  }
}

// 使用示例
const container = document.getElementById('list-container');
const virtualList = new VirtualList(
  container,
  50, // 每项高度50px
  10000, // 总共10000项
  (element, index) => {
    element.textContent = `Item ${index}`;
    element.style.backgroundColor = index % 2 ? '#f0f0f0' : '#fff';
  }
);

4. 性能监控与分析

4.1 使用性能API

// 测量代码执行时间
performance.mark('start');
// 执行代码...
performance.mark('end');
performance.measure('执行时间', 'start', 'end');
const measures = performance.getEntriesByType('measure');
console.log(`执行时间: ${measures[0].duration}ms`);

// 监控资源加载时间
window.addEventListener('load', () => {
  const resources = performance.getEntriesByType('resource');
  resources.forEach(resource => {
    console.log(`${resource.name}: ${resource.duration}ms`);
  });
});

4.2 使用Chrome开发者工具

// 使用console.time测量性能
console.time('操作');
// 执行代码...
console.timeEnd('操作');

// 分析内存使用
console.memory;

5. 框架特定优化

5.1 React优化

// 使用React.memo避免不必要的重渲染
const MemoizedComponent = React.memo(function MyComponent(props) {
  // 组件逻辑
});

// 使用useCallback缓存函数引用
const memoizedCallback = useCallback(() => {
  doSomething(a, b);
}, [a, b]);

// 使用useMemo缓存计算结果
const memoizedValue = useMemo(() => {
  return computeExpensiveValue(a, b);
}, [a, b]);

5.2 Vue优化

// 使用v-once渲染一次性内容
<span v-once>{{ neverChanges }}</span>

// 使用v-memo缓存模板部分
<div v-memo="[valueA, valueB]">
  <!-- 只有当valueA或valueB变化时才会更新 -->
</div>

// 使用计算属性缓存结果
computed: {
  expensiveComputation() {
    return this.items.filter(/* 复杂计算 */);
  }
}

6. 安全最佳实践

6.1 安全的第三方库使用

// 使用子资源完整性(SRI)确保第三方库未被篡改
<script 
  src="https://cdn.example.com/library.js" 
  integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC" 
  crossorigin="anonymous">
</script>

6.2 安全的正则表达式

防止正则表达式拒绝服务攻击(ReDoS):

// 危险: 容易受到ReDoS攻击的正则表达式
const dangerousRegex = /^(a+)+$/;
dangerousRegex.test('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!');

// 安全: 限制重复次数
const safeRegex = /^(a+){1,10}$/;

6.3 iframe安全

<!-- 使用sandbox和CSP限制iframe权限 -->
<iframe 
  src="https://third-party.com" 
  sandbox="allow-scripts allow-same-origin" 
  allow="camera 'none'; microphone 'none'; geolocation 'none'"
></iframe>

7. 总结

JavaScript性能优化和安全是相辅相成的。良好的性能优化实践通常也能提高代码的安全性,而安全的代码实现也往往有助于提升应用性能。开发者应当在项目初期就考虑这两个方面,并在整个开发周期中持续关注和改进。

关键点总结:

  1. 性能优化

    • 减少DOM操作,使用事件委托
    • 优化资源加载,使用懒加载
    • 避免布局抖动,使用requestAnimationFrame
    • 利用Web Workers处理复杂计算
    • 实现虚拟列表处理大量数据
  2. 安全防护

    • 防御XSS攻击,转义用户输入
    • 防御CSRF攻击,使用Token和SameSite Cookie
    • 安全处理JSON,避免使用eval
    • 防止原型污染
    • 使用HTTPS和内容安全策略(CSP)

通过持续学习和应用这些最佳实践,可以构建既高效又安全的JavaScript应用。

结语
感谢您的阅读!期待您的一键三连!欢迎指正!

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

【Air】

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值