引言

ES6(ECMAScript 2015)是JavaScript历史上最重要的更新之一,它引入了大量新特性,极大地提升了语言的表达力和开发效率。然而,随着新特性的引入,开发者也面临着配置不当、兼容性问题和潜在陷阱。本文将全面解析ES6的配置亮点,提供详细的代码示例,帮助你避免常见陷阱,并显著提升代码质量。

1. Babel配置:现代JavaScript的基石

1.1 为什么需要Babel?

ES6引入了许多新语法,但并非所有浏览器都完全支持。Babel是一个JavaScript编译器,能将ES6+代码转换为向后兼容的ES5代码,确保代码在各种环境中运行一致。

1.2 基础配置示例

首先,安装必要的包:

npm install --save-dev @babel/core @babel/cli @babel/preset-env

创建 .babelrc 配置文件:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "browsers": ["last 2 versions", "ie >= 11"]
        },
        "useBuiltIns": "usage",
        "corejs": 3
      }
    ]
  ],
  "plugins": []
}

配置详解:

  • targets:指定目标环境,这里配置为最近2个版本的浏览器和IE11+
  • useBuiltIns: "usage":根据代码实际使用情况按需引入polyfill
  • corejs: 3:使用core-js 3版本提供完整的ES6+ polyfill

1.3 常见陷阱与解决方案

陷阱1:polyfill重复引入

// 错误做法:在多个文件中手动引入
import '@babel/polyfill';
import 'core-js/stable';
import 'regenerator-runtime/runtime';

// 正确做法:让Babel自动处理
// 在babel配置中设置 useBuiltIns: "usage"

陷阱2:忽略浏览器列表配置

// 在package.json中配置browserslist,让多个工具共享配置
"browserslist": [
  "> 1%",
  "last 2 versions",
  "not dead",
  "not ie <= 10"
]

2. 模块系统配置与最佳实践

2.1 ES6模块 vs CommonJS

ES6模块是静态的,这带来了更好的优化和tree-shaking支持。

// ES6模块 - 静态导入/导出
// math.js
export const add = (a, b) => a + b;
export const multiply = (a, b) => a * b;

// app.js
import { add, multiply } from './math.js';

// CommonJS - 动态导入/导出
// math.js
module.exports = {
  add: (a, b) => a + b,
  multiply: (a, b) => a * b
};

// app.js
const math = require('./math.js');

2.2 动态导入与代码分割

动态import()是ES2020特性,但常与ES6配合使用,实现按需加载:

// 配置Webpack支持动态导入
// webpack.config.js
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: '[name].[contenthash].js',
    chunkFilename: '[name].[contenthash].chunk.js',
    path: path.resolve(__dirname, 'dist')
  },
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        }
      }
    }
  }
};

// 使用动态导入实现代码分割
async function loadComponent() {
  const { default: Component } = await import('./HeavyComponent.js');
  return Component;
}

// 在React中的应用
const HeavyComponent = lazy(() => import('./HeavyComponent.js'));

2.3 避免循环依赖

循环依赖是模块系统的常见陷阱:

// a.js
import { b } from './b.js';
export const a = () => {
  console.log('a called');
  b();
};

// b.js
import { a } from './a.js';
export const b = () => {
  console.log('b called');
  // 注意:此时a可能还未完全初始化
  // a(); // 可能导致错误
};

// 解决方案:重构代码结构,或使用依赖注入
// 使用中间模块解耦
// utils.js
export const callB = () => {
  const { b } = require('./b.js');
  b();
};

// a.js
import { callB } from './utils.js';
export const a = () => {
  console.log('a called');
  callB();
};

3. 解构赋值与默认值配置

3.1 对象解构

// 基础用法
const user = { name: 'Alice', age: 30, city: 'NYC' };
const { name, age } = user;

// 重命名
const { name: userName, age: userAge } = user;

// 默认值
const { name = 'Guest', country = 'USA' } = user;

// 嵌套解构
const data = {
  user: { id: 1, profile: { email: 'test@example.com' } }
};
const { user: { profile: { email } } } = data;

3.2 数组解构

const [first, second, ...rest] = [1, 2, 3, 4, 5];
// first=1, second=2, rest=[3,4,5]

// 交换变量
let a = 1, b = 2;
[a, b] = [b, a]; // a=2, b=1

// 函数参数解构
function processUser({ name, age = 18 }) {
  console.log(`${name} is ${age} years old`);
}
processUser({ name: 'Bob' }); // Bob is 18 years old

3.3 避免解构陷阱

陷阱:解构null/undefined

// 错误:解构null会抛出错误
const { name } = null; // TypeError

// 正确做法:提供默认值或检查
const { name } = null || {};
const safeDestructure = (obj) => {
  if (!obj) return {};
  const { name, age } = obj;
  return { name, age };
};

4. 箭头函数与this绑定

4.1 箭头函数特性

// 词法this:继承外层作用域的this值
const obj = {
  name: 'MyObject',
  regularFunc: function() {
    console.log(this.name); // 'MyObject'
  },
  arrowFunc: () => {
    // this指向定义时的上下文(通常是window/global)
    console.log(this.name); // undefined(在严格模式下)
  }
};

// 箭头函数没有自己的this、arguments、super或new.target
const createCounter = () => {
  let count = 0;
  return {
    increment: () => count++,
    getValue: () => count
  };
};

4.2 事件处理器中的陷阱

// 陷阱:在类方法中使用箭头函数作为回调
class Button {
  constructor() {
    this.count = 0;
    // 正确:箭头函数绑定外层this
    this.handleClick = () => {
      this.count++;
      console.log(`Clicked ${this.count} times`);
    };
  }
  
  // 错误:普通函数作为回调会丢失this
  handleClickWrong() {
    this.count++;
  }
}

// 在React中的应用
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
    // 推荐:在构造函数中绑定或使用箭头函数属性
    this.handleClick = this.handleClick.bind(this);
  }
  
  handleClick() {
    this.setState(prevState => ({ count: prevState.count + 1 }));
  }
  
  render() {
    return <button onClick={this.handleClick}>Click me</button>;
  }
}

5. Promise与异步编程配置

5.1 Promise基础与配置

// 创建Promise
const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    Math.random() > 0.5 ? resolve('Success') : reject('Error');
  }, 1000);
});

// 链式调用
myPromise
  .then(result => {
    console.log(result);
    return result.toUpperCase();
  })
  .then(upperResult => {
    console.log(upperResult);
  })
  .catch(error => {
    console.error('Error:', error);
  })
  .finally(() => {
    console.log('Cleanup');
  });

5.2 Promise.all与错误处理

// Promise.all:并行执行多个Promise
const fetchUser = () => Promise.resolve({ id: 1, name: 'Alice' });
const fetchPosts = () => Promise.resolve([{ id: 1, title: 'Post 1' }]);
const fetchComments = () => Promise.reject('Network Error');

// 正确处理Promise.all的错误
Promise.all([
  fetchUser(),
  fetchPosts(),
  fetchComments().catch(err => ({ error: err })) // 捕获单个错误
]).then(([user, posts, comments]) => {
  console.log('User:', user);
  // 处理可能的错误
  if (comments.error) {
    console.warn('Comments failed:', comments.error);
  }
});

// Promise.allSettled:获取所有结果(包括失败)
Promise.allSettled([
  fetchUser(),
  fetchPosts(),
  fetchComments()
]).then(results => {
  results.forEach(result => {
    if (result.status === 'fulfilled') {
      console.log('Success:', result.value);
    } else {
      console.log('Failed:', result.reason);
    }
  });
});

5.3 避免Promise陷阱

陷阱1:忘记return

// 错误:链式调用中忘记return
fetch('/api/data')
  .then(response => response.json())
  .then(data => {
    // 这里没有return,下一个then接收到的是undefined
    process(data);
  })
  .then(result => {
    console.log(result); // undefined
  });

// 正确:
fetch('/api/data')
  .then(response => response.json())
  => {
    process(data);
    return data; // 必须return
  })
  .then(result => {
    console.log(result); // 正确的数据
  });

陷阱2:Promise构造反模式

// 错误:不必要的Promise包装
function getData() {
  return new Promise((resolve, reject) => {
    fetch('/api/data')
      .then(response => response.json())
      .then(resolve)
      .catch(reject);
  });
}

// 正确:直接返回Promise
function getData() {
  return fetch('/api/data').then(response => response.json());
}

6. async/await配置与错误处理

6.1 async/await基础

// async函数总是返回Promise
async function fetchUserData(userId) {
  try {
    const response = await fetch(`/api/users/${userId}`);
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }
    const data = await response.json();
    return data;
  } catch (1error) {
    console.error('Fetch failed:', error);
    throw error; // 重新抛出以便调用者处理
  }
}

// 使用
fetchUserData(1)
  .then(user => console.log(user))
  .catch(err => console.error(err));

6.2 并行与串行执行

// 串行执行:依赖前一个结果
async function sequential() {
  const user = await fetchUser();
  const posts = await fetchPosts(user.id); // 依赖user
  return { user, posts };
}

// 并行执行:无依赖关系
async function parallel() {
  const [user, config] = await Promise.all([
    fetchUser(),
    fetchConfig()
  ]);
  return { user, config };
}

// 部分并行
async function mixed() {
  const userPromise = fetchUser();
  const configPromise = fetchConfig();
  
  const user = await userPromise;
  const config = await configPromise;
  
  const posts = await fetchPosts(user.id);
  return { user, config, posts };
}

6.3 高级错误处理模式

// 重试机制
async function fetchWithRetry(url, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url);
      return await response.json();
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
    }
  }
}

// 超时控制
async function fetchWithTimeout(url, timeout = 5000) {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeout);
  
  try {
    const response = await fetch(url, { signal: controller.signal });
    clearTimeout(timeoutId);
    return await response.json();
  } catch (error) {
    if (error.name === 'AbortError') {
      throw new Error('Request timed out');
    }
    throw error;
  }
}

7. 类与继承配置

7.1 ES6类基础

class Animal {
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(`${this.name} makes a sound`);
  }
  
  // 静态方法
  static create(name) {
    return new Animal(name);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name); // 必须调用super
    this.breed = breed;
  }
  
  speak() {
    console.log(`${this.name} barks`);
  }
  
  // Getter
  get description() {
    return `${this.name} is a ${this.breed}`;
  }
}

// 使用
const dog = new Dog('Rex', 'German Shepherd');
dog.speak(); // Rex barks
console.log(dog.description); // Rex is a German Shepherd

7.2 私有字段(ES2022)

class Counter {
  #count = 0; // 私有字段
  
  increment() {
    this.#count++;
  }
  
  getValue() {
    return this.#count;
  }
}

const counter = new Counter();
counter.increment();
console.log(counter.getValue()); // 1
console.log(counter.#count); // SyntaxError: Private field '#count' must be declared in an enclosing class

7.3 避免类的陷阱

陷阱:this丢失

class Timer {
  constructor() {
    this.seconds = 0;
    // 陷阱:setInterval的回调会丢失this
    setInterval(this.tick, 1000); // this.tick中的this是undefined
  }
  
  tick() {
    this.seconds++; // TypeError: Cannot read property 'seconds' of undefined
  }
}

// 解决方案1:箭头函数绑定
class TimerFixed1 {
  constructor() {
    this.seconds = 0;
    setInterval(() => this.tick(), 1000);
  }
  tick() { this.seconds++; }
}

// 解决方案2:bind
class TimerFixed2 {
  constructor() {
    this.seconds = 0;
    this.tick = this.tick.bind(this);
    setInterval(this.tick, 1000);
  }
  tick() { this秒数++; }
}

8. 模板字符串与标签模板

8.1 基础用法

// 多行字符串
const multiline = `This is a
multiline
string`;

// 插值
const name = 'Alice';
const age = 30;
const message = `Hello, ${name}. You are ${age} years old.`;

// 嵌入表达式
const cost = 100;
const total = `Total: $${cost * 1.1}`; // "Total: $110"

8.2 标签模板

// 创建标签函数
function highlight(strings, ...values) {
  return strings.reduce((result, str, i) => {
    const value = values[i] || '';
    return result + str + `<mark>${value}</mark>`;
  }, '');
}

// 使用
const user = { name: 'Alice', role: 'Admin' };
const html = highlight`User: ${user.name}, Role: ${user.role}`;
// Result: "User: <mark>Alice</mark>, Role: <mark>Admin</mark>"

// 安全HTML转义
function safeHTML(strings, ...values) {
  const escape = (str) => String(str).replace(/[&<>"']/g, s => ({
    '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;'
  }[s]));
  
  return strings.reduce((result, str, i) => {
    return result + str + escape(values[i] || '');
  }, '');
}

9. Map/Set与数据结构优化

9.1 Map vs Object

// Map的优势:键可以是任意类型
const map = new Map();
map.set('string', 'value');
map.set(123, 'number key');
map.set({}, 'object key');
map.set(() => {}, 'function key');

// Map保持插入顺序
const orderedMap = new Map([
  ['a', 1],
  ['b', 2],
  ['c', 3]
]);
console.log([...orderedMap]); // [['a',1], ['b',2], ['c',3]]

// Map有更好的性能(频繁增删场景)
// Object需要O(n)查找,Map是O(1)

9.2 Set与数组去重

// 数组去重
const numbers = [1, 2, 2, 3, 4, 4, 5];
const unique = [...new Set(numbers)]; // [1,2,3,4,5]

// Set运算
const setA = new Set([1, 2, 3]);
const setB = new Set([2, 3, 4]);

// 并集
const union = new Set([...setA, ...setB]); // {1,2,3,4}

// 交集
const intersection = new Set([...setA].filter(x => setB.has(x))); // {2,3}

// 差集
const difference = new Set([...setA].filter(x => !setB.has(x))); // {1}

10. 配置工具链与构建优化

10.1 Webpack配置示例

// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  mode: 'production',
  entry: {
    main: './src/index.js',
    vendor: ['react', 'react-dom']
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[contenthash].js',
    chunkFilename: '[name].[contenthash].chunk.js',
    publicPath: '/'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
            cacheDirectory: true // 启用缓存加速构建
          }
        }
      }
    ]
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          reuseExistingChunk: true
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    },
    runtimeChunk: 'single'
  },
  // 性能优化
  performance: {
    maxAssetSize: 512000,
    maxEntrypointSize: 512000,
    hints: 'warning'
  }
};

10.2 ESLint配置

// .eslintrc.json
{
  "env": {
    "browser": true,
    "es2021": true,
    "node": true
  },
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:import/recommended",
    "plugin:prettier/recommended"
  ],
  "parserOptions": {
    "ecmaVersion": 2021,
    "sourceType": "module",
    "ecmaFeatures": {
      "jsx": true
    }
  },
  "plugins": ["react", "import", "prettier"],
  "rules": {
    "prettier/prettier": "error",
    "no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
    "no-console": "warn",
    "prefer-const": "error",
    "no-var": "error",
    "react/prop-types": "off", // 使用TypeScript时关闭
    "import/no-unresolved": "error",
    "import/no-anonymous-default-export": "warn"
  },
  "settings": {
    "react": {
      "version": "detect"
    }
  }
}

10.3 Prettier配置

// .prettierrc
{
  "printWidth": 80,
  "tabWidth": 2,
  "useTabs": false,
  "semi": true,
  "singleQuote": true,
  "trailingComma": "es5",
  "bracketSpacing": true,
  "arrowParens": "always",
  "endOfLine": "lf"
}

11. 性能优化与最佳实践

11.1 避免不必要的对象创建

// 不好的做法:每次调用都创建新对象
function processUser(user) {
  return {
    ...user,
    processed: true
  };
}

// 更好的做法:如果不需要新对象,直接修改
function processUserInPlace(user) {
  user.processed = true;
  return user;
}

// 或者使用Object.freeze防止意外修改
const DEFAULT_CONFIG = Object.freeze({
  timeout: 5000,
  retries: 3
});

11.2 使用Object.freeze优化

// 冻结对象可以提升性能(V8优化)
const CONFIG = Object.freeze({
  API_URL: 'https://api.example.com',
  TIMEOUT: 5000,
  MAX_RETRIES: 3
});

// 冻结嵌套对象
function deepFreeze(obj) {
  Object.freeze(obj);
  Object.getOwnPropertyNames(obj).forEach(prop => {
    if (obj[prop] && typeof obj[prop] === 'object' && !Object.isFrozen(obj[prop])) {
      deepFreeze(obj[prop]);
    }
  });
  return obj;
}

11.3 使用WeakMap/WeakSet避免内存泄漏

// 传统Map会导致内存泄漏
const cache = new Map();
function process(obj) {
  if (cache.has(obj)) return cache.get(obj);
  const result = heavyComputation(obj);
  cache.set(obj, obj);
  return result;
}

// 使用WeakMap:当obj被垃圾回收时,条目自动删除
const weakCache = new WeakMap();
function processWeak(obj) {
  if (weakCache.has(obj)) return weakCache.get(obj);
  const result = heavyComputation(obj);
  weakCache.set(obj, result);
  return result;
}

12. 测试与调试配置

12.1 Jest配置

// jest.config.js
module.exports = {
  testEnvironment: 'jsdom',
  collectCoverageFrom: [
    'src/**/*.{js,jsx}',
    '!src/index.js',
    '!src/reportWebVitals.js'
  ],
  coverageThreshold: {
    global: {
      branches: 80,
      functions: 80,
      lines: 80,
      statements: 80
    }
  },
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1'
  },
  setupFilesAfterEnv: ['<rootDir>/src/setupTests.js'],
  transform: {
    '^.+\\.[jt]sx?$': 'babel-jest'
  },
  // 监听配置
  watchPlugins: [
    'jest-watch-typeahead/filename',
    'jest-watch-typeahead/testname'
  ]
};

12.2 测试示例

// sum.js
export const sum = (a, b) => a + b;

// sum.test.js
import { sum } from './sum';

describe('sum function', () => {
  test('adds 1 + 2 to equal 3', () => {
    expect(sum(1, 2)).toBe(3);
  });
  
  test('handles decimal numbers', () => {
    expect(sum(0.1, 0.2)).toBeCloseTo(0.3);
  });
});

// 异步测试
async function fetchData() {
  const response = await fetch('/api/data');
  return response.json();
}

test('fetchData returns expected data', async () => {
  // Mock fetch
  global.fetch = jest.fn(() =>
    Promise.resolve({
      json: () => Promise.resolve({ id: 1, name: 'Test' })
    })
  );
  
  const data = await fetchData();
  expect(data).toEqual({ id: 1, name: 'Test' });
});

13. 总结与最佳实践清单

13.1 配置检查清单

✅ Babel配置

  • 使用 @babel/preset-env 并配置正确的targets
  • 启用 useBuiltIns: "usage" 按需polyfill
  • 配置browserslist共享配置

✅ 模块系统

  • 优先使用ES6模块
  • 使用动态import()进行代码分割
  • 避免循环依赖

✅ 异步编程

  • 使用async/await替代回调地狱
  • 使用Promise.allSettled处理并行异步操作
  • 实现重试和超时机制

✅ 性能优化

  • 使用Map/Set替代普通对象/数组(特定场景)
  • 使用Object.freeze优化常量对象
  • 使用WeakMap避免内存泄漏

✅ 代码质量

  • 配置ESLint和Prettier
  • 编写单元测试
  • 使用TypeScript增强类型安全

13.2 常见陷阱总结

  1. polyfill管理:避免手动引入,让Babel自动处理
  2. this绑定:在类方法中使用箭头函数或bind
  3. Promise链:记得return值
  4. 解构null:提供默认值或检查
  5. 循环依赖:重构代码结构
  6. 内存泄漏:使用WeakMap/WeakSet存储对象引用
  7. 性能:避免不必要的对象创建,使用Object.freeze

通过遵循这些配置和最佳实践,你可以充分利用ES6的特性,编写出更高效、更可靠、更易维护的JavaScript代码。# ES6配置亮点全解析:如何避免常见陷阱并提升代码质量

引言

ES6(ECMAScript 2015)是JavaScript历史上最重要的更新之一,它引入了大量新特性,极大地提升了语言的表达力和开发效率。然而,随着新特性的引入,开发者也面临着配置不当、兼容性问题和潜在陷阱。本文将全面解析ES6的配置亮点,提供详细的代码示例,帮助你避免常见陷阱,并显著提升代码质量。

1. Babel配置:现代JavaScript的基石

1.1 为什么需要Babel?

ES6引入了许多新语法,但并非所有浏览器都完全支持。Babel是一个JavaScript编译器,能将ES6+代码转换为向后兼容的ES5代码,确保代码在各种环境中运行一致。

1.2 基础配置示例

首先,安装必要的包:

npm install --save-dev @babel/core @babel/cli @babel/preset-env

创建 .babelrc 配置文件:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "browsers": ["last 2 versions", "ie >= 11"]
        },
        "useBuiltIns": "usage",
        "corejs": 3
      }
    ]
  ],
  "plugins": []
}

配置详解:

  • targets:指定目标环境,这里配置为最近2个版本的浏览器和IE11+
  • useBuiltIns: "usage":根据代码实际使用情况按需引入polyfill
  • corejs: 3:使用core-js 3版本提供完整的ES6+ polyfill

1.3 常见陷阱与解决方案

陷阱1:polyfill重复引入

// 错误做法:在多个文件中手动引入
import '@babel/polyfill';
import 'core-js/stable';
import 'regenerator-runtime/runtime';

// 正确做法:让Babel自动处理
// 在babel配置中设置 useBuiltIns: "usage"

陷阱2:忽略浏览器列表配置

// 在package.json中配置browserslist,让多个工具共享配置
"browserslist": [
  "> 1%",
  "last 2 versions",
  "not dead",
  "not ie <= 10"
]

2. 模块系统配置与最佳实践

2.1 ES6模块 vs CommonJS

ES6模块是静态的,这带来了更好的优化和tree-shaking支持。

// ES6模块 - 静态导入/导出
// math.js
export const add = (a, b) => a + b;
export const multiply = (a, b) => a * b;

// app.js
import { add, multiply } from './math.js';

// CommonJS - 动态导入/导出
// math.js
module.exports = {
  add: (a, b) => a + b,
  multiply: (a, b) => a * b
};

// app.js
const math = require('./math.js');

2.2 动态导入与代码分割

动态import()是ES2020特性,但常与ES6配合使用,实现按需加载:

// 配置Webpack支持动态导入
// webpack.config.js
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: '[name].[contenthash].js',
    chunkFilename: '[name].[contenthash].chunk.js',
    path: path.resolve(__dirname, 'dist')
  },
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        }
      }
    }
  }
};

// 使用动态导入实现代码分割
async function loadComponent() {
  const { default: Component } = await import('./HeavyComponent.js');
  return Component;
}

// 在React中的应用
const HeavyComponent = lazy(() => import('./HeavyComponent.js'));

2.3 避免循环依赖

循环依赖是模块系统的常见陷阱:

// a.js
import { b } from './b.js';
export const a = () => {
  console.log('a called');
  b();
};

// b.js
import { a } from './a.js';
export const b = () => {
  console.log('b called');
  // 注意:此时a可能还未完全初始化
  // a(); // 可能导致错误
};

// 解决方案:重构代码结构,或使用依赖注入
// 使用中间模块解耦
// utils.js
export const callB = () => {
  const { b } = require('./b.js');
  b();
};

// a.js
import { callB } from './utils.js';
export const a = () => {
  console.log('a called');
  callB();
};

3. 解构赋值与默认值配置

3.1 对象解构

// 基础用法
const user = { name: 'Alice', age: 30, city: 'NYC' };
const { name, age } = user;

// 重命名
const { name: userName, age: userAge } = user;

// 默认值
const { name = 'Guest', country = 'USA' } = user;

// 嵌套解构
const data = {
  user: { id: 1, profile: { email: 'test@example.com' } }
};
const { user: { profile: { email } } } = data;

3.2 数组解构

const [first, second, ...rest] = [1, 2, 3, 4, 5];
// first=1, second=2, rest=[3,4,5]

// 交换变量
let a = 1, b = 2;
[a, b] = [b, a]; // a=2, b=1

// 函数参数解构
function processUser({ name, age = 18 }) {
  console.log(`${name} is ${age} years old`);
}
processUser({ name: 'Bob' }); // Bob is 18 years old

3.3 避免解构陷阱

陷阱:解构null/undefined

// 错误:解构null会抛出错误
const { name } = null; // TypeError

// 正确做法:提供默认值或检查
const { name } = null || {};
const safeDestructure = (obj) => {
  if (!obj) return {};
  const { name, age } = obj;
  return { name, age };
};

4. 箭头函数与this绑定

4.1 箭头函数特性

// 词法this:继承外层作用域的this值
const obj = {
  name: 'MyObject',
  regularFunc: function() {
    console.log(this.name); // 'MyObject'
  },
  arrowFunc: () => {
    // this指向定义时的上下文(通常是window/global)
    console.log(this.name); // undefined(在严格模式下)
  }
};

// 箭头函数没有自己的this、arguments、super或new.target
const createCounter = () => {
  let count = 0;
  return {
    increment: () => count++,
    getValue: () => count
  };
};

4.2 事件处理器中的陷阱

// 陷阱:在类方法中使用箭头函数作为回调
class Button {
  constructor() {
    this.count = 0;
    // 正确:箭头函数绑定外层this
    this.handleClick = () => {
      this.count++;
      console.log(`Clicked ${this.count} times`);
    };
  }
  
  // 错误:普通函数作为回调会丢失this
  handleClickWrong() {
    this.count++;
  }
}

// 在React中的应用
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
    // 推荐:在构造函数中绑定或使用箭头函数属性
    this.handleClick = this.handleClick.bind(this);
  }
  
  handleClick() {
    this.setState(prevState => ({ count: prevState.count + 1 }));
  }
  
  render() {
    return <button onClick={this.handleClick}>Click me</button>;
  }
}

5. Promise与异步编程配置

5.1 Promise基础与配置

// 创建Promise
const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    Math.random() > 0.5 ? resolve('Success') : reject('Error');
  }, 1000);
});

// 链式调用
myPromise
  .then(result => {
    console.log(result);
    return result.toUpperCase();
  })
  .then(upperResult => {
    console.log(upperResult);
  })
  .catch(error => {
    console.error('Error:', error);
  })
  .finally(() => {
    console.log('Cleanup');
  });

5.2 Promise.all与错误处理

// Promise.all:并行执行多个Promise
const fetchUser = () => Promise.resolve({ id: 1, name: 'Alice' });
const fetchPosts = () => Promise.resolve([{ id: 1, title: 'Post 1' }]);
const fetchComments = () => Promise.reject('Network Error');

// 正确处理Promise.all的错误
Promise.all([
  fetchUser(),
  fetchPosts(),
  fetchComments().catch(err => ({ error: err })) // 捕获单个错误
]).then(([user, posts, comments]) => {
  console.log('User:', user);
  // 处理可能的错误
  if (comments.error) {
    console.warn('Comments failed:', comments.error);
  }
});

// Promise.allSettled:获取所有结果(包括失败)
Promise.allSettled([
  fetchUser(),
  fetchPosts(),
  fetchComments()
]).then(results => {
  results.forEach(result => {
    if (result.status === 'fulfilled') {
      console.log('Success:', result.value);
    } else {
      console.log('Failed:', result.reason);
    }
  });
});

5.3 避免Promise陷阱

陷阱1:忘记return

// 错误:链式调用中忘记return
fetch('/api/data')
  .then(response => response.json())
  .then(data => {
    // 这里没有return,下一个then接收到的是undefined
    process(data);
  })
  .then(result => {
    console.log(result); // undefined
  });

// 正确:
fetch('/api/data')
  .then(response => response.json())
  => {
    process(data);
    return data; // 必须return
  })
  .then(result => {
    console.log(result); // 正确的数据
  });

陷阱2:Promise构造反模式

// 错误:不必要的Promise包装
function getData() {
  return new Promise((resolve, reject) => {
    fetch('/api/data')
      .then(response => response.json())
      .then(resolve)
      .catch(reject);
  });
}

// 正确:直接返回Promise
function getData() {
  return fetch('/api/data').then(response => response.json());
}

6. async/await配置与错误处理

6.1 async/await基础

// async函数总是返回Promise
async function fetchUserData(userId) {
  try {
    const response = await fetch(`/api/users/${userId}`);
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Fetch failed:', error);
    throw error; // 重新抛出以便调用者处理
  }
}

// 使用
fetchUserData(1)
  .then(user => console.log(user))
  .catch(err => console.error(err));

6.2 并行与串行执行

// 串行执行:依赖前一个结果
async function sequential() {
  const user = await fetchUser();
  const posts = await fetchPosts(user.id); // 依赖user
  return { user, posts };
}

// 并行执行:无依赖关系
async function parallel() {
  const [user, config] = await Promise.all([
    fetchUser(),
    fetchConfig()
  ]);
  return { user, config };
}

// 部分并行
async function mixed() {
  const userPromise = fetchUser();
  const configPromise = fetchConfig();
  
  const user = await userPromise;
  const config = await configPromise;
  
  const posts = await fetchPosts(user.id);
  return { user, config, posts };
}

6.3 高级错误处理模式

// 重试机制
async function fetchWithRetry(url, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url);
      return await response.json();
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
    }
  }
}

// 超时控制
async function fetchWithTimeout(url, timeout = 5000) {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeout);
  
  try {
    const response = await fetch(url, { signal: controller.signal });
    clearTimeout(timeoutId);
    return await response.json();
  } catch (error) {
    if (error.name === 'AbortError') {
      throw new Error('Request timed out');
    }
    throw error;
  }
}

7. 类与继承配置

7.1 ES6类基础

class Animal {
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(`${this.name} makes a sound`);
  }
  
  // 静态方法
  static create(name) {
    return new Animal(name);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name); // 必须调用super
    this.breed = breed;
  }
  
  speak() {
    console.log(`${this.name} barks`);
  }
  
  // Getter
  get description() {
    return `${this.name} is a ${this.breed}`;
  }
}

// 使用
const dog = new Dog('Rex', 'German Shepherd');
dog.speak(); // Rex barks
console.log(dog.description); // Rex is a German Shepherd

7.2 私有字段(ES2022)

class Counter {
  #count = 0; // 私有字段
  
  increment() {
    this.#count++;
  }
  
  getValue() {
    return this.#count;
  }
}

const counter = new Counter();
counter.increment();
console.log(counter.getValue()); // 1
console.log(counter.#count); // SyntaxError: Private field '#count' must be declared in an enclosing class

7.3 避免类的陷阱

陷阱:this丢失

class Timer {
  constructor() {
    this.seconds = 0;
    // 陷阱:setInterval的回调会丢失this
    setInterval(this.tick, 1000); // this.tick中的this是undefined
  }
  
  tick() {
    this.seconds++; // TypeError: Cannot read property 'seconds' of undefined
  }
}

// 解决方案1:箭头函数绑定
class TimerFixed1 {
  constructor() {
    this.seconds = 0;
    setInterval(() => this.tick(), 1000);
  }
  tick() { this.seconds++; }
}

// 解决方案2:bind
class TimerFixed2 {
  constructor() {
    this.seconds = 0;
    this.tick = this.tick.bind(this);
    setInterval(this.tick, 1000);
  }
  tick() { this秒数++; }
}

8. 模板字符串与标签模板

8.1 基础用法

// 多行字符串
const multiline = `This is a
multiline
string`;

// 插值
const name = 'Alice';
const age = 30;
const message = `Hello, ${name}. You are ${age} years old.`;

// 嵌入表达式
const cost = 100;
const total = `Total: $${cost * 1.1}`; // "Total: $110"

8.2 标签模板

// 创建标签函数
function highlight(strings, ...values) {
  return strings.reduce((result, str, i) => {
    const value = values[i] || '';
    return result + str + `<mark>${value}</mark>`;
  }, '');
}

// 使用
const user = { name: 'Alice', role: 'Admin' };
const html = highlight`User: ${user.name}, Role: ${user.role}`;
// Result: "User: <mark>Alice</mark>, Role: <mark>Admin</mark>"

// 安全HTML转义
function safeHTML(strings, ...values) {
  const escape = (str) => String(str).replace(/[&<>"']/g, s => ({
    '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;'
  }[s]));
  
  return strings.reduce((result, str, i) => {
    return result + str + escape(values[i] || '');
  }, '');
}

9. Map/Set与数据结构优化

9.1 Map vs Object

// Map的优势:键可以是任意类型
const map = new Map();
map.set('string', 'value');
map.set(123, 'number key');
map.set({}, 'object key');
map.set(() => {}, 'function key');

// Map保持插入顺序
const orderedMap = new Map([
  ['a', 1],
  ['b', 2],
  ['c', 3]
]);
console.log([...orderedMap]); // [['a',1], ['b',2], ['c',3]]

// Map有更好的性能(频繁增删场景)
// Object需要O(n)查找,Map是O(1)

9.2 Set与数组去重

// 数组去重
const numbers = [1, 2, 2, 3, 4, 4, 5];
const unique = [...new Set(numbers)]; // [1,2,3,4,5]

// Set运算
const setA = new Set([1, 2, 3]);
const setB = new Set([2, 3, 4]);

// 并集
const union = new Set([...setA, ...setB]); // {1,2,3,4}

// 交集
const intersection = new Set([...setA].filter(x => setB.has(x))); // {2,3}

// 差集
const difference = new Set([...setA].filter(x => !setB.has(x))); // {1}

10. 配置工具链与构建优化

10.1 Webpack配置示例

// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  mode: 'production',
  entry: {
    main: './src/index.js',
    vendor: ['react', 'react-dom']
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[contenthash].js',
    chunkFilename: '[name].[contenthash].chunk.js',
    publicPath: '/'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
            cacheDirectory: true // 启用缓存加速构建
          }
        }
      }
    ]
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          reuseExistingChunk: true
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    },
    runtimeChunk: 'single'
  },
  // 性能优化
  performance: {
    maxAssetSize: 512000,
    maxEntrypointSize: 512000,
    hints: 'warning'
  }
};

10.2 ESLint配置

// .eslintrc.json
{
  "env": {
    "browser": true,
    "es2021": true,
    "node": true
  },
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:import/recommended",
    "plugin:prettier/recommended"
  ],
  "parserOptions": {
    "ecmaVersion": 2021,
    "sourceType": "module",
    "ecmaFeatures": {
      "jsx": true
    }
  },
  "plugins": ["react", "import", "prettier"],
  "rules": {
    "prettier/prettier": "error",
    "no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
    "no-console": "warn",
    "prefer-const": "error",
    "no-var": "error",
    "react/prop-types": "off", // 使用TypeScript时关闭
    "import/no-unresolved": "error",
    "import/no-anonymous-default-export": "warn"
  },
  "settings": {
    "react": {
      "version": "detect"
    }
  }
}

10.3 Prettier配置

// .prettierrc
{
  "printWidth": 80,
  "tabWidth": 2,
  "useTabs": false,
  "semi": true,
  "singleQuote": true,
  "trailingComma": "es5",
  "bracketSpacing": true,
  "arrowParens": "always",
  "endOfLine": "lf"
}

11. 性能优化与最佳实践

11.1 避免不必要的对象创建

// 不好的做法:每次调用都创建新对象
function processUser(user) {
  return {
    ...user,
    processed: true
  };
}

// 更好的做法:如果不需要新对象,直接修改
function processUserInPlace(user) {
  user.processed = true;
  return user;
}

// 或者使用Object.freeze防止意外修改
const DEFAULT_CONFIG = Object.freeze({
  timeout: 5000,
  retries: 3
});

11.2 使用Object.freeze优化

// 冻结对象可以提升性能(V8优化)
const CONFIG = Object.freeze({
  API_URL: 'https://api.example.com',
  TIMEOUT: 5000,
  MAX_RETRIES: 3
});

// 冻结嵌套对象
function deepFreeze(obj) {
  Object.freeze(obj);
  Object.getOwnPropertyNames(obj).forEach(prop => {
    if (obj[prop] && typeof obj[prop] === 'object' && !Object.isFrozen(obj[prop])) {
      deepFreeze(obj[prop]);
    }
  });
  return obj;
}

11.3 使用WeakMap/WeakSet避免内存泄漏

// 传统Map会导致内存泄漏
const cache = new Map();
function process(obj) {
  if (cache.has(obj)) return cache.get(obj);
  const result = heavyComputation(obj);
  cache.set(obj, obj);
  return result;
}

// 使用WeakMap:当obj被垃圾回收时,条目自动删除
const weakCache = new WeakMap();
function processWeak(obj) {
  if (weakCache.has(obj)) return weakCache.get(obj);
  const result = heavyComputation(obj);
  weakCache.set(obj, result);
  return result;
}

12. 测试与调试配置

12.1 Jest配置

// jest.config.js
module.exports = {
  testEnvironment: 'jsdom',
  collectCoverageFrom: [
    'src/**/*.{js,jsx}',
    '!src/index.js',
    '!src/reportWebVitals.js'
  ],
  coverageThreshold: {
    global: {
      branches: 80,
      functions: 80,
      lines: 80,
      statements: 80
    }
  },
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1'
  },
  setupFilesAfterEnv: ['<rootDir>/src/setupTests.js'],
  transform: {
    '^.+\\.[jt]sx?$': 'babel-jest'
  },
  // 监听配置
  watchPlugins: [
    'jest-watch-typeahead/filename',
    'jest-watch-typeahead/testname'
  ]
};

12.2 测试示例

// sum.js
export const sum = (a, b) => a + b;

// sum.test.js
import { sum } from './sum';

describe('sum function', () => {
  test('adds 1 + 2 to equal 3', () => {
    expect(sum(1, 2)).toBe(3);
  });
  
  test('handles decimal numbers', () => {
    expect(sum(0.1, 0.2)).toBeCloseTo(0.3);
  });
});

// 异步测试
async function fetchData() {
  const response = await fetch('/api/data');
  return response.json();
}

test('fetchData returns expected data', async () => {
  // Mock fetch
  global.fetch = jest.fn(() =>
    Promise.resolve({
      json: () => Promise.resolve({ id: 1, name: 'Test' })
    })
  );
  
  const data = await fetchData();
  expect(data).toEqual({ id: 1, name: 'Test' });
});

13. 总结与最佳实践清单

13.1 配置检查清单

✅ Babel配置

  • 使用 @babel/preset-env 并配置正确的targets
  • 启用 useBuiltIns: "usage" 按需polyfill
  • 配置browserslist共享配置

✅ 模块系统

  • 优先使用ES6模块
  • 使用动态import()进行代码分割
  • 避免循环依赖

✅ 异步编程

  • 使用async/await替代回调地狱
  • 使用Promise.allSettled处理并行异步操作
  • 实现重试和超时机制

✅ 性能优化

  • 使用Map/Set替代普通对象/数组(特定场景)
  • 使用Object.freeze优化常量对象
  • 使用WeakMap避免内存泄漏

✅ 代码质量

  • 配置ESLint和Prettier
  • 编写单元测试
  • 使用TypeScript增强类型安全

13.2 常见陷阱总结

  1. polyfill管理:避免手动引入,让Babel自动处理
  2. this绑定:在类方法中使用箭头函数或bind
  3. Promise链:记得return值
  4. 解构null:提供默认值或检查
  5. 循环依赖:重构代码结构
  6. 内存泄漏:使用WeakMap/WeakSet存储对象引用
  7. 性能:避免不必要的对象创建,使用Object.freeze

通过遵循这些配置和最佳实践,你可以充分利用ES6的特性,编写出更高效、更可靠、更易维护的JavaScript代码。