JavaScript 设计模式与安全实践指南

一、设计模式在 JavaScript 中的应用

1. 工厂模式:灵活的对象创建

工厂模式通过将对象创建逻辑封装在函数中,提供了一种创建对象的统一接口。

class Car {
  constructor(model, year) {
    this.model = model;
    this.year = year;
  }
}

class Truck {
  constructor(model, year) {
    this.model = model;
    this.year = year;
  }
}

function vehicleFactory(type, model, year) {
  switch(type) {
    case 'car':
      return new Car(model, year);
    case 'truck':
      return new Truck(model, year);
    default:
      throw new Error('Unknown vehicle type');
  }
}

const myCar = vehicleFactory('car', 'Tesla', 2023);
const myTruck = vehicleFactory('truck', 'Ford', 2022);

实践建议

  • 当对象创建逻辑复杂时使用工厂模式
  • 适用于需要根据不同条件创建不同对象的场景
  • 有利于代码解耦,符合单一职责原则

2. 单例模式:全局唯一实例

单例模式确保一个类只有一个实例,并提供全局访问点。

class DatabaseConnection {
  constructor() {
    if (DatabaseConnection.instance) {
      return DatabaseConnection.instance;
    }
    
    this.connection = this.createConnection();
    DatabaseConnection.instance = this;
    
    return this;
  }
  
  createConnection() {
    // 模拟数据库连接
    console.log('Creating new database connection...');
    return { status: 'connected' };
  }
}

const db1 = new DatabaseConnection();
const db2 = new DatabaseConnection();

console.log(db1 === db2); // true

实践建议

  • 适用于数据库连接、日志记录器等需要全局唯一实例的场景
  • 注意线程安全问题(在JavaScript中通常不是问题)
  • 考虑使用模块系统(如ES Modules)实现更简单的单例

3. 观察者模式:事件驱动的解耦

观察者模式定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会得到通知。

class Subject {
  constructor() {
    this.observers = [];
  }
  
  subscribe(observer) {
    this.observers.push(observer);
  }
  
  unsubscribe(observer) {
    this.observers = this.observers.filter(obs => obs !== observer);
  }
  
  notify(data) {
    this.observers.forEach(observer => observer.update(data));
  }
}

class Observer {
  update(data) {
    console.log(`Received data: ${data}`);
  }
}

const subject = new Subject();
const observer1 = new Observer();
const observer2 = new Observer();

subject.subscribe(observer1);
subject.subscribe(observer2);

subject.notify('Hello World!');
// 输出:
// Received data: Hello World!
// Received data: Hello World!

实践建议

  • 适用于需要实现松耦合的事件处理系统
  • 现代前端框架(如React、Vue)的状态管理库(Redux、Vuex)都基于此模式
  • 注意及时取消订阅避免内存泄漏

二、JavaScript 安全实践

1. XSS 防御:保护你的应用

跨站脚本攻击(XSS)是JavaScript应用最常见的安全威胁之一。

防御措施

// 1. 转义HTML内容
function escapeHtml(unsafe) {
  return unsafe
    .replace(/&/g, "&")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#039;");
}

// 2. 使用textContent而不是innerHTML
element.textContent = userInput;

// 3. 现代框架的内置防护
// React/Vue/Angular等框架默认会转义动态内容

实践建议

  • 始终对用户输入进行验证和转义
  • 使用Content Security Policy (CSP)作为额外防护层
  • 避免使用eval()innerHTML等危险API

2. CSRF 防护:阻止跨站请求伪造

// 1. 使用CSRF令牌
// 后端生成令牌并包含在表单中
<form action="/transfer" method="POST">
  <input type="hidden" name="_csrf" value="token-value">
  <!-- 其他表单字段 -->
</form>

// 2. 设置SameSite Cookie属性
// 服务器设置Cookie时
Set-Cookie: sessionId=abc123; SameSite=Strict; Secure

// 3. 验证请求来源
app.use((req, res, next) => {
  const allowedOrigins = ['https://yourdomain.com'];
  const origin = req.headers.origin;
  
  if (allowedOrigins.includes(origin)) {
    res.setHeader('Access-Control-Allow-Origin', origin);
  }
  next();
});

实践建议

  • 结合多种防护措施(令牌+SameSite+来源验证)
  • 对于敏感操作考虑增加二次验证
  • 定期审计API端点安全性

3. Content Security Policy (CSP) 配置

CSP通过白名单机制限制资源加载,有效防御XSS攻击。

<!-- 示例CSP策略 -->
<meta http-equiv="Content-Security-Policy" 
      content="default-src 'self';
               script-src 'self' 'unsafe-inline' https://apis.google.com;
               img-src 'self' https://*.example.com;
               style-src 'self' 'unsafe-inline';
               font-src 'self' https://fonts.gstatic.com;
               connect-src 'self' https://api.example.com;
               frame-src 'none';
               object-src 'none';">

实践建议

  • 从严格策略开始,逐步放宽必要规则
  • 使用report-uri收集违规报告
  • 在生产环境前充分测试所有功能

三、现代工具链配置技巧

1. Webpack 优化配置

// webpack.config.js
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: '[name].[contenthash].bundle.js',
    path: path.resolve(__dirname, 'dist'),
    clean: true,
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
          },
        },
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      },
    ],
  },
  optimization: {
    splitChunks: {
      chunks: 'all',
    },
    runtimeChunk: 'single',
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
    }),
  ],
};

实践建议

  • 使用代码分割提升加载性能
  • 长期缓存利用contenthash
  • 开发环境启用HMR(热模块替换)

2. Babel 转译配置

// .babelrc
{
  "presets": [
    ["@babel/preset-env", {
      "targets": {
        "browsers": ["last 2 versions", "not dead"]
      },
      "useBuiltIns": "usage",
      "corejs": 3
    }]
  ],
  "plugins": [
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-transform-runtime"
  ]
}

实践建议

  • 根据目标浏览器配置preset-env
  • 使用core-js自动polyfill
  • 开发库时使用transform-runtime避免全局污染

3. ESLint 代码规范

// .eslintrc.js
module.exports = {
  env: {
    browser: true,
    es2021: true,
  },
  extends: [
    'eslint:recommended',
    'plugin:react/recommended',
    'airbnb',
    'prettier',
  ],
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: 12,
    sourceType: 'module',
  },
  plugins: ['react', 'prettier'],
  rules: {
    'prettier/prettier': 'error',
    'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx'] }],
    'import/prefer-default-export': 'off',
    'no-param-reassign': ['error', { props: false }],
  },
};

实践建议

  • 结合Prettier统一代码风格
  • 根据团队习惯定制规则
  • 在CI/CD流程中加入lint检查

四、框架特性深度解析

1. React Hooks 高级用法

import { useState, useEffect, useMemo, useCallback } from 'react';

function UserList({ users }) {
  const [searchTerm, setSearchTerm] = useState('');
  const [filteredUsers, setFilteredUsers] = useState([]);
  
  // 使用useMemo优化计算
  const sortedUsers = useMemo(() => {
    return [...users].sort((a, b) => a.name.localeCompare(b.name));
  }, [users]);
  
  // 使用useCallback记忆函数
  const handleSearch = useCallback((term) => {
    const filtered = sortedUsers.filter(user => 
      user.name.toLowerCase().includes(term.toLowerCase())
    );
    setFilteredUsers(filtered);
  }, [sortedUsers]);
  
  // 副作用处理
  useEffect(() => {
    handleSearch(searchTerm);
  }, [searchTerm, handleSearch]);
  
  return (
    <div>
      <input 
        type="text" 
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
      />
      <ul>
        {filteredUsers.map(user => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
}

实践建议

  • 使用useMemo缓存昂贵计算
  • 使用useCallback避免不必要的重新渲染
  • 按逻辑拆分自定义Hook提高可复用性

2. Vue 响应式原理剖析

Vue 3使用Proxy实现响应式系统:

const reactive = (target) => {
  return new Proxy(target, {
    get(obj, key) {
      track(obj, key); // 依赖收集
      return Reflect.get(obj, key);
    },
    set(obj, key, value) {
      Reflect.set(obj, key, value);
      trigger(obj, key); // 触发更新
      return true;
    },
  });
};

function track(target, key) {
  // 记录当前正在运行的effect
  if (activeEffect) {
    let depsMap = targetMap.get(target);
    if (!depsMap) {
      targetMap.set(target, (depsMap = new Map()));
    }
    let dep = depsMap.get(key);
    if (!dep) {
      depsMap.set(key, (dep = new Set()));
    }
    dep.add(activeEffect);
  }
}

function trigger(target, key) {
  const depsMap = targetMap.get(target);
  if (!depsMap) return;
  
  const effects = depsMap.get(key);
  effects && effects.forEach(effect => effect());
}

实践建议

  • 理解响应式原理有助于性能优化
  • 避免在大型数据结构上使用响应式
  • 使用shallowRef/shallowReactive减少不必要的响应式开销

总结

JavaScript生态系统不断发展,掌握核心设计模式和安全实践是成为高级开发者的关键。通过:

  1. 合理应用设计模式提高代码质量
  2. 严格实施安全防护措施
  3. 熟练使用现代工具链提升开发效率
  4. 深入理解框架特性实现最佳实践

可以构建出更健壮、更安全的JavaScript应用。记住,良好的架构和安全意识应该从项目开始就纳入考虑,而不是事后补救。

评论已关闭