JavaScript ES6+ 新特性

章节介绍

ES6(ECMAScript 2015)是 JavaScript 的一个重要版本更新,引入了许多新特性和语法糖,极大地提升了开发效率和代码可读性。本教程将详细介绍 ES6+ 的核心新特性,帮助您编写更现代、更优雅的 JavaScript 代码。

核心知识点

let 和 const

ES6 引入了 letconst 关键字,用于声明块级作用域的变量。

// let 声明变量
let name = '张三';
name = '李四';  // 可以重新赋值

// const 声明常量
const PI = 3.14159;
// PI = 3.14;  // 错误:不能重新赋值

// 块级作用域
function testScope() {
  if (true) {
    let blockVar = '块级变量';
    const blockConst = '块级常量';
    console.log(blockVar);    // 块级变量
    console.log(blockConst);  // 块级常量
  }
  
  // console.log(blockVar);    // 错误:无法访问
  // console.log(blockConst);  // 错误:无法访问
}

testScope();

// const 对象的属性可以修改
const person = { name: '张三', age: 25 };
person.age = 26;  // 可以修改属性
console.log(person);  // { name: '张三', age: 26 }

// 不能重新赋值整个对象
// person = { name: '李四' };  // 错误

箭头函数

箭头函数提供了更简洁的函数语法,并且不绑定自己的 this

// 传统函数
function add(a, b) {
  return a + b;
}

// 箭头函数
const add2 = (a, b) => a + b;

// 单参数可以省略括号
const double = x => x * 2;

// 多行代码需要大括号和 return
const calculate = (a, b) => {
  const sum = a + b;
  const product = a * b;
  return { sum, product };
};

console.log(add(5, 3));      // 8
console.log(add2(5, 3));     // 8
console.log(double(5));      // 10
console.log(calculate(5, 3));  // { sum: 8, product: 15 }

// 箭头函数不绑定 this
const person = {
  name: '张三',
  
  // 传统函数:this 指向调用者
  greet: function() {
    console.log(`你好,我是 ${this.name}`);
  },
  
  // 箭头函数:this 指向外层作用域
  greet2: () => {
    console.log(`你好,我是 ${this.name}`);  // undefined
  },
  
  // 在对象方法中使用箭头函数(不推荐)
  greet3: function() {
    setTimeout(() => {
      console.log(`延迟问候:${this.name}`);  // 正确捕获 this
    }, 1000);
  }
};

person.greet();   // 你好,我是 张三
person.greet2();  // 你好,我是 undefined
person.greet3();  // 延迟问候:张三

模板字符串

模板字符串使用反引号(`)包裹,支持多行字符串和变量插值。

// 基本使用
const name = '张三';
const age = 25;

// 传统字符串拼接
const message1 = '你好,我是 ' + name + ',今年 ' + age + ' 岁。';

// 模板字符串
const message2 = `你好,我是 ${name},今年 ${age} 岁。`;

console.log(message1);  // 你好,我是 张三,今年 25 岁。
console.log(message2);  // 你好,我是 张三,今年 25 岁。

// 多行字符串
const html = `
  <div class="card">
    <h2>${name}</h2>
    <p>年龄:${age}</p>
  </div>
`;

console.log(html);

// 表达式求值
const a = 10;
const b = 20;
console.log(`${a} + ${b} = ${a + b}`);  // 10 + 20 = 30

// 调用函数
function greet(name) {
  return `你好,${name}!`;
}
console.log(`问候:${greet('李四')}`);  // 问候:你好,李四!

// 嵌套模板字符串
const product = {
  name: 'iPhone 13',
  price: 6999,
  discount: 0.9
};

const description = `
  商品:${product.name}
  原价:¥${product.price}
  现价:¥${product.price * product.discount}
  节省:¥${product.price * (1 - product.discount)}
`;

console.log(description);

解构赋值

解构赋值允许我们从数组或对象中提取值并赋值给变量。

数组解构

// 基本解构
const colors = ['红色', '绿色', '蓝色'];
const [first, second, third] = colors;

console.log(first);   // 红色
console.log(second);  // 绿色
console.log(third);   // 蓝色

// 忽略某些元素
const [primary, , tertiary] = colors;
console.log(primary);   // 红色
console.log(tertiary);  // 蓝色

// 使用剩余元素
const [firstColor, ...restColors] = colors;
console.log(firstColor);  // 红色
console.log(restColors);  // ['绿色', '蓝色']

// 默认值
const [a, b, c = '黄色'] = colors;
console.log(c);  // 蓝色(如果 colors 只有 2 个元素,c 会是 '黄色')

// 交换变量
let x = 1;
let y = 2;
[x, y] = [y, x];
console.log(x);  // 2
console.log(y);  // 1

对象解构

// 基本解构
const person = {
  name: '张三',
  age: 25,
  city: '北京'
};

const { name, age, city } = person;
console.log(name);  // 张三
console.log(age);   // 25
console.log(city);  // 北京

// 重命名
const { name: userName, age: userAge } = person;
console.log(userName);  // 张三
console.log(userAge);   // 25

// 默认值
const { name, country = '中国' } = person;
console.log(name);     // 张三
console.log(country);  // 中国

// 嵌套解构
const user = {
  name: '张三',
  address: {
    city: '北京',
    district: '朝阳区',
    street: '建国路'
  }
};

const { name, address: { city, district } } = user;
console.log(name);     // 张三
console.log(city);     // 北京
console.log(district);  // 朝阳区

// 函数参数解构
function greet({ name, age }) {
  console.log(`你好,${name}!你今年 ${age} 岁。`);
}

greet(person);  // 你好,张三!你今年 25 岁。

默认参数

函数参数可以设置默认值。

// 基本默认参数
function greet(name = '朋友', message = '你好') {
  console.log(`${message},${name}!`);
}

greet();                    // 你好,朋友!
greet('张三');              // 你好,张三!
greet('李四', '欢迎');      // 欢迎,李四!

// 解构参数的默认值
function createUser({ name = '匿名', age = 0, city = '北京' } = {}) {
  return { name, age, city };
}

console.log(createUser());  // { name: '匿名', age: 0, city: '北京' }
console.log(createUser({ name: '张三' }));  // { name: '张三', age: 0, city: '北京' }
console.log(createUser({ name: '李四', age: 25 }));  // { name: '李四', age: 25, city: '北京' }

// 参数表达式
function getDefaultValue() {
  console.log('计算默认值');
  return 100;
}

function test(value = getDefaultValue()) {
  console.log(value);
}

test();        // 计算默认值 \n 100
test(50);      // 50(不计算默认值)

展开运算符和剩余参数

展开运算符(...)用于展开数组或对象,剩余参数用于收集多个参数。

// 数组展开
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];

const combined = [...arr1, ...arr2];
console.log(combined);  // [1, 2, 3, 4, 5, 6]

// 数组复制
const original = [1, 2, 3];
const copy = [...original];
console.log(copy);  // [1, 2, 3]

// 数组展开作为函数参数
function sum(a, b, c) {
  return a + b + c;
}

const numbers = [1, 2, 3];
console.log(sum(...numbers));  // 6

// 对象展开
const obj1 = { name: '张三', age: 25 };
const obj2 = { city: '北京', email: 'zhangsan@example.com' };

const merged = { ...obj1, ...obj2 };
console.log(merged);  // { name: '张三', age: 25, city: '北京', email: 'zhangsan@example.com' }

// 对象复制
const originalObj = { name: '张三', age: 25 };
const copyObj = { ...originalObj };
console.log(copyObj);  // { name: '张三', age: 25 }

// 剩余参数
function sumAll(...numbers) {
  return numbers.reduce((sum, num) => sum + num, 0);
}

console.log(sumAll(1, 2, 3, 4, 5));  // 15

function greet(greeting, ...names) {
  names.forEach(name => {
    console.log(`${greeting},${name}!`);
  });
}

greet('你好', '张三', '李四', '王五');
// 你好,张三!
// 你好,李四!
// 你好,王五!

// 数组解构中的剩余参数
const [first, ...rest] = [1, 2, 3, 4, 5];
console.log(first);  // 1
console.log(rest);   // [2, 3, 4, 5]

// 对象解构中的剩余参数
const { name, ...restInfo } = { name: '张三', age: 25, city: '北京' };
console.log(name);      // 张三
console.log(restInfo);  // { age: 25, city: '北京' }

对象字面量增强

ES6 增强了对象字面量的语法。

// 属性简写
const name = '张三';
const age = 25;

const person = {
  name,  // 等同于 name: name
  age    // 等同于 age: age
};

console.log(person);  // { name: '张三', age: 25 }

// 方法简写
const calculator = {
  add(a, b) {
    return a + b;
  },
  
  subtract(a, b) {
    return a - b;
  }
};

console.log(calculator.add(5, 3));       // 8
console.log(calculator.subtract(5, 3));  // 2

// 计算属性名
const prop = 'dynamic';
const value = '动态值';

const obj = {
  [prop]: value,
  ['computed' + 'Property']: '计算属性'
};

console.log(obj);  // { dynamic: '动态值', computedProperty: '计算属性' }

// 对象方法简写中的 this
const person2 = {
  name: '张三',
  
  greet() {
    console.log(`你好,我是 ${this.name}`);
  },
  
  // 箭头函数方法(不推荐)
  greet2: () => {
    console.log(`你好,我是 ${this.name}`);  // undefined
  }
};

person2.greet();   // 你好,我是 张三
person2.greet2();  // 你好,我是 undefined

数组新方法

ES6 引入了许多新的数组方法。

// Array.from():将类数组对象或可迭代对象转换为数组
const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
const array1 = Array.from(arrayLike);
console.log(array1);  // ['a', 'b', 'c']

const string = 'hello';
const array2 = Array.from(string);
console.log(array2);  // ['h', 'e', 'l', 'l', 'o']

// Array.of():创建包含参数的新数组
const array3 = Array.of(1, 2, 3);
console.log(array3);  // [1, 2, 3]

// find():找到第一个符合条件的元素
const numbers = [1, 2, 3, 4, 5];
const found = numbers.find(num => num > 3);
console.log(found);  // 4

// findIndex():找到第一个符合条件的元素的索引
const index = numbers.findIndex(num => num > 3);
console.log(index);  // 3

// includes():检查数组是否包含某个元素
const hasThree = numbers.includes(3);
console.log(hasThree);  // true

const hasSix = numbers.includes(6);
console.log(hasSix);  // false

// keys()、values()、entries()
const fruits = ['苹果', '香蕉', '橙子'];

console.log([...fruits.keys()]);    // [0, 1, 2]
console.log([...fruits.values()]);  // ['苹果', '香蕉', '橙子']
console.log([...fruits.entries()]);  // [[0, '苹果'], [1, '香蕉'], [2, '橙子']]

// fill():填充数组
const filled = new Array(5).fill(0);
console.log(filled);  // [0, 0, 0, 0, 0]

const partialFilled = [1, 2, 3, 4, 5];
partialFilled.fill(0, 1, 3);  // 从索引 1 到 3(不包括 3)填充 0
console.log(partialFilled);  // [1, 0, 0, 4, 5]

字符串新方法

ES6 引入了许多新的字符串方法。

// startsWith():检查字符串是否以指定字符串开头
const str = 'Hello World';

console.log(str.startsWith('Hello'));  // true
console.log(str.startsWith('World'));  // false

// endsWith():检查字符串是否以指定字符串结尾
console.log(str.endsWith('World'));  // true
console.log(str.endsWith('Hello'));  // false

// includes():检查字符串是否包含指定字符串
console.log(str.includes('World'));  // true
console.log(str.includes('world'));  // false(区分大小写)

// repeat():重复字符串
const repeated = 'abc'.repeat(3);
console.log(repeated);  // abcabcabc

// padStart():在字符串开头填充
const padded1 = '5'.padStart(2, '0');
console.log(padded1);  // 05

const padded2 = 'test'.padStart(10, '-');
console.log(padded2);  // ------test

// padEnd():在字符串结尾填充
const padded3 = '5'.padEnd(2, '0');
console.log(padded3);  // 50

const padded4 = 'test'.padEnd(10, '-');
console.log(padded4);  // test------

// trimStart() / trimEnd():去除字符串开头或结尾的空白
const spaced = '  hello  ';
console.log(spaced.trimStart());  // 'hello  '
console.log(spaced.trimEnd());    // '  hello'

// 字符串解构
const [firstChar, ...restChars] = 'hello';
console.log(firstChar);  // h
console.log(restChars.join(''));  // ello

Promise

Promise 是处理异步操作的重要特性。

// 创建 Promise
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const success = true;
    
    if (success) {
      resolve('操作成功!');
    } else {
      reject('操作失败!');
    }
  }, 1000);
});

// 使用 Promise
promise
  .then(result => {
    console.log(result);  // 操作成功!
  })
  .catch(error => {
    console.log(error);
  })
  .finally(() => {
    console.log('操作完成');
  });

// Promise 链
function fetchData() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({ id: 1, name: '张三' });
    }, 1000);
  });
}

function fetchDetails(userId) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({ userId, age: 25, city: '北京' });
    }, 1000);
  });
}

fetchData()
  .then(user => {
    console.log('用户信息:', user);
    return fetchDetails(user.id);
  })
  .then(details => {
    console.log('详细信息:', details);
  })
  .catch(error => {
    console.log('错误:', error);
  });

// Promise.all()
const promise1 = Promise.resolve(3);
const promise2 = 1337;
const promise3 = new Promise(resolve => setTimeout(resolve, 1000, 'foo'));

Promise.all([promise1, promise2, promise3]).then(values => {
  console.log(values);  // [3, 1337, 'foo']
});

// Promise.race()
const promise4 = new Promise(resolve => setTimeout(resolve, 500, 'one'));
const promise5 = new Promise(resolve => setTimeout(resolve, 100, 'two'));

Promise.race([promise4, promise5]).then(value => {
  console.log(value);  // two(先完成的 Promise)
});

async/await

async/await 是基于 Promise 的语法糖,让异步代码看起来像同步代码。

// 基本使用
async function fetchData() {
  const response = await fetch('https://api.example.com/data');
  const data = await response.json();
  return data;
}

// 错误处理
async function fetchDataWithErrorHandling() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    return data;
  } catch (error) {
    console.log('获取数据失败:', error);
    throw error;
  }
}

// 并发请求
async function fetchMultipleData() {
  const [user, posts, comments] = await Promise.all([
    fetch('https://api.example.com/user').then(res => res.json()),
    fetch('https://api.example.com/posts').then(res => res.json()),
    fetch('https://api.example.com/comments').then(res => res.json())
  ]);
  
  return { user, posts, comments };
}

// 顺序请求
async function fetchSequentialData() {
  const userResponse = await fetch('https://api.example.com/user');
  const user = await userResponse.json();
  
  const postsResponse = await fetch(`https://api.example.com/posts?userId=${user.id}`);
  const posts = await postsResponse.json();
  
  return { user, posts };
}

// 箭头函数 async
const asyncFetch = async () => {
  const data = await fetchData();
  console.log(data);
};

类(Class)

ES6 引入了 class 关键字,提供了更清晰的面向对象编程语法。

// 基本类定义
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  
  greet() {
    console.log(`你好,我是 ${this.name},今年 ${this.age} 岁。`);
  }
  
  updateAge(newAge) {
    this.age = newAge;
    console.log(`年龄已更新为 ${newAge}`);
  }
}

const person = new Person('张三', 25);
person.greet();           // 你好,我是 张三,今年 25 岁。
person.updateAge(26);     // 年龄已更新为 26

// 继承
class Student extends Person {
  constructor(name, age, grade) {
    super(name, age);  // 调用父类构造函数
    this.grade = grade;
  }
  
  study() {
    console.log(`${this.name} 正在学习,年级:${this.grade}`);
  }
  
  // 重写父类方法
  greet() {
    super.greet();  // 调用父类方法
    console.log(`我是 ${this.grade} 年级的学生。`);
  }
}

const student = new Student('李四', 18, '高三');
student.greet();  // 你好,我是 李四,今年 18 岁。\n我是 高三 年级的学生。
student.study();  // 李四 正在学习,年级:高三

// 静态方法
class Calculator {
  static add(a, b) {
    return a + b;
  }
  
  static subtract(a, b) {
    return a - b;
  }
}

console.log(Calculator.add(5, 3));       // 8
console.log(Calculator.subtract(5, 3));  // 2

// Getter 和 Setter
class Circle {
  constructor(radius) {
    this._radius = radius;
  }
  
  get radius() {
    return this._radius;
  }
  
  set radius(value) {
    if (value > 0) {
      this._radius = value;
    }
  }
  
  get area() {
    return Math.PI * this._radius * this._radius;
  }
  
  get circumference() {
    return 2 * Math.PI * this._radius;
  }
}

const circle = new Circle(5);
console.log(circle.radius);  // 5
console.log(circle.area);    // 78.53981633974483
circle.radius = 10;
console.log(circle.area);    // 314.1592653589793

模块化

ES6 引入了原生的模块化支持。

// 导出变量和函数(math.js)
export const PI = 3.14159;

export function add(a, b) {
  return a + b;
}

export function subtract(a, b) {
  return a - b;
}

export default function multiply(a, b) {
  return a * b;
}

// 导入(app.js)
import multiply, { add, subtract, PI } from './math.js';

console.log(add(5, 3));       // 8
console.log(subtract(5, 3));  // 2
console.log(multiply(5, 3));  // 15
console.log(PI);              // 3.14159

// 重命名导入
import { add as sum } from './math.js';
console.log(sum(5, 3));  // 8

// 导入整个模块
import * as math from './math.js';
console.log(math.add(5, 3));  // 8

实用案例分析

案例 1:使用 ES6+ 特性重构代码

使用 ES6+ 新特性重构传统 JavaScript 代码。

// 传统代码
function createUser(name, age, city) {
  var user = {
    name: name,
    age: age,
    city: city
  };
  return user;
}

function greet(user) {
  return '你好,' + user.name + '!你今年 ' + user.age + ' 岁。';
}

function calculateTotal(items) {
  var total = 0;
  for (var i = 0; i < items.length; i++) {
    total = total + items[i].price * items[i].quantity;
  }
  return total;
}

// ES6+ 重构后的代码
const createUser = (name, age, city) => ({ name, age, city });

const greet = ({ name, age }) => `你好,${name}!你今年 ${age} 岁。`;

const calculateTotal = items => 
  items.reduce((total, { price, quantity }) => total + price * quantity, 0);

// 使用示例
const user = createUser('张三', 25, '北京');
console.log(greet(user));

const items = [
  { name: 'iPhone 13', price: 6999, quantity: 1 },
  { name: 'AirPods Pro', price: 1999, quantity: 2 }
];
console.log(calculateTotal(items));  // 10997

案例 2:异步数据获取

使用 async/await 处理异步数据获取。

// 模拟 API 请求
function fetchUser(userId) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({
        id: userId,
        name: '张三',
        email: 'zhangsan@example.com'
      });
    }, 500);
  });
}

function fetchPosts(userId) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve([
        { id: 1, userId, title: '第一篇文章' },
        { id: 2, userId, title: '第二篇文章' }
      ]);
    }, 800);
  });
}

function fetchComments(postId) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve([
        { id: 1, postId, content: '很好的文章!' },
        { id: 2, postId, content: '学到了很多' }
      ]);
    }, 600);
  });
}

// 使用 async/await 获取完整数据
async function getUserData(userId) {
  try {
    const user = await fetchUser(userId);
    const posts = await fetchPosts(userId);
    
    const postsWithComments = await Promise.all(
      posts.map(async post => ({
        ...post,
        comments: await fetchComments(post.id)
      }))
    );
    
    return {
      user,
      posts: postsWithComments
    };
  } catch (error) {
    console.log('获取数据失败:', error);
    throw error;
  }
}

// 使用示例
getUserData(1)
  .then(data => {
    console.log('用户数据:', data.user);
    console.log('文章数据:', data.posts);
  })
  .catch(error => {
    console.log('错误:', error);
  });

案例 3:使用类和模块化构建应用

使用类和模块化构建一个简单的待办事项应用。

// todo.js
export class Todo {
  constructor(title, description = '') {
    this.id = Date.now();
    this.title = title;
    this.description = description;
    this.completed = false;
    this.createdAt = new Date();
  }
  
  toggle() {
    this.completed = !this.completed;
  }
  
  update(title, description) {
    this.title = title;
    this.description = description;
  }
}

// todo-list.js
import { Todo } from './todo.js';

export class TodoList {
  constructor() {
    this.todos = [];
  }
  
  add(title, description) {
    const todo = new Todo(title, description);
    this.todos.push(todo);
    return todo;
  }
  
  remove(id) {
    this.todos = this.todos.filter(todo => todo.id !== id);
  }
  
  toggle(id) {
    const todo = this.todos.find(todo => todo.id === id);
    if (todo) {
      todo.toggle();
    }
  }
  
  update(id, title, description) {
    const todo = this.todos.find(todo => todo.id === id);
    if (todo) {
      todo.update(title, description);
    }
  }
  
  getAll() {
    return this.todos;
  }
  
  getCompleted() {
    return this.todos.filter(todo => todo.completed);
  }
  
  getPending() {
    return this.todos.filter(todo => !todo.completed);
  }
  
  clearCompleted() {
    this.todos = this.todos.filter(todo => !todo.completed);
  }
}

// app.js
import { TodoList } from './todo-list.js';

const todoList = new TodoList();

// 添加待办事项
todoList.add('学习 Node.js', '完成 Node.js 教程');
todoList.add('练习编程', '每天写代码 2 小时');
todoList.add('阅读技术书籍', '每周阅读一本技术书籍');

console.log('所有待办事项:');
console.log(todoList.getAll());

// 标记完成
todoList.toggle(1);
console.log('\n标记第一个待办事项为完成:');
console.log(todoList.getAll());

// 获取未完成的待办事项
console.log('\n未完成的待办事项:');
console.log(todoList.getPending());

// 清除已完成的待办事项
todoList.clearCompleted();
console.log('\n清除已完成的待办事项:');
console.log(todoList.getAll());

代码示例

示例 1:使用 ES6+ 特性处理数据

const users = [
  { id: 1, name: '张三', age: 25, city: '北京', scores: [85, 90, 88] },
  { id: 2, name: '李四', age: 30, city: '上海', scores: [92, 88, 95] },
  { id: 3, name: '王五', age: 28, city: '广州', scores: [78, 85, 82] },
  { id: 4, name: '赵六', age: 35, city: '深圳', scores: [95, 92, 98] }
];

// 计算每个用户的平均分
const usersWithAverage = users.map(user => ({
  ...user,
  averageScore: user.scores.reduce((sum, score) => sum + score, 0) / user.scores.length
}));

console.log('用户平均分:');
console.log(usersWithAverage);

// 筛选平均分大于 85 的用户
const highScoreUsers = usersWithAverage.filter(user => user.averageScore > 85);
console.log('\n高分用户:');
console.log(highScoreUsers);

// 按城市分组
const usersByCity = users.reduce((groups, user) => {
  const city = user.city;
  if (!groups[city]) {
    groups[city] = [];
  }
  groups[city].push(user);
  return groups;
}, {});

console.log('\n按城市分组:');
console.log(usersByCity);

// 找到最高分的用户
const topUser = usersWithAverage.reduce((max, user) => 
  user.averageScore > max.averageScore ? user : max
);

console.log('\n最高分用户:');
console.log(topUser);

示例 2:使用解构和展开操作对象

// 合并多个对象
const defaults = {
  theme: 'light',
  language: 'zh-CN',
  fontSize: 14,
  notifications: true
};

const userPreferences = {
  theme: 'dark',
  fontSize: 16
};

const systemSettings = {
  language: 'en-US'
};

const finalSettings = {
  ...defaults,
  ...userPreferences,
  ...systemSettings
};

console.log('最终设置:');
console.log(finalSettings);

// 选择性更新对象
const originalUser = {
  id: 1,
  name: '张三',
  age: 25,
  email: 'zhangsan@example.com',
  city: '北京',
  country: '中国'
};

const updates = {
  age: 26,
  city: '上海'
};

const updatedUser = {
  ...originalUser,
  ...updates
};

console.log('\n更新后的用户:');
console.log(updatedUser);

// 排除某些属性
const { id, ...userWithoutId } = originalUser;
console.log('\n不包含 ID 的用户信息:');
console.log(userWithoutId);

// 动态属性名
const prefix = 'user';
const key = 'name';
const value = '张三';

const dynamicObject = {
  [`${prefix}_${key}`]: value
};

console.log('\n动态属性对象:');
console.log(dynamicObject);

示例 3:使用类和继承

// 基类:动物
class Animal {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  
  speak() {
    console.log(`${this.name} 发出了声音`);
  }
  
  eat() {
    console.log(`${this.name} 正在吃东西`);
  }
  
  sleep() {
    console.log(`${this.name} 正在睡觉`);
  }
}

// 子类:狗
class Dog extends Animal {
  constructor(name, age, breed) {
    super(name, age);
    this.breed = breed;
  }
  
  speak() {
    console.log(`${this.name}(${this.breed})汪汪叫`);
  }
  
  fetch() {
    console.log(`${this.name} 正在捡球`);
  }
}

// 子类:猫
class Cat extends Animal {
  constructor(name, age, color) {
    super(name, age);
    this.color = color;
  }
  
  speak() {
    console.log(`${this.name}(${this.color})喵喵叫`);
  }
  
  scratch() {
    console.log(`${this.name} 正在抓东西`);
  }
}

// 使用示例
const dog = new Dog('旺财', 3, '金毛');
const cat = new Cat('咪咪', 2, '白色');

console.log('狗的行为:');
dog.speak();   // 旺财(金毛)汪汪叫
dog.eat();     // 旺财 正在吃东西
dog.fetch();   // 旺财 正在捡球

console.log('\n猫的行为:');
cat.speak();   // 咪咪(白色)喵喵叫
cat.eat();     // 咪咪 正在吃东西
cat.scratch(); // 咪咪 正在抓东西

实现技巧与注意事项

ES6+ 使用建议

  1. 优先使用 const:只有在需要重新赋值时才使用 let
  2. 使用箭头函数:特别是在回调函数中
  3. 使用解构赋值:提高代码可读性
  4. 使用模板字符串:比字符串拼接更清晰
  5. 使用 async/await:比 Promise 链更易读

兼容性考虑

  1. 检查浏览器支持:使用 Babel 转译不支持的特性
  2. 使用 Polyfill:为不支持新特性的环境提供替代实现
  3. 渐进增强:在新特性不可用时提供降级方案

性能优化

  1. 避免过度使用展开运算符:大对象展开可能影响性能
  2. 合理使用解构:频繁解构可能影响性能
  3. 使用适当的数据结构:Map/Set 在某些情况下比对象/数组更高效

常见问题与解决方案

问题 1:箭头函数中的 this 指向

// 问题代码
const obj = {
  name: '张三',
  
  init() {
    setTimeout(() => {
      console.log(this.name);  // undefined
    }, 1000);
  }
};

// 解决方案 1:使用箭头函数
const obj = {
  name: '张三',
  
  init() {
    setTimeout(() => {
      console.log(this.name);  // 张三
    }, 1000);
  }
};

// 解决方案 2:使用 bind
const obj = {
  name: '张三',
  
  init() {
    setTimeout(function() {
      console.log(this.name);  // 张三
    }.bind(this), 1000);
  }
};

问题 2:async/await 中的错误处理

// 问题代码:没有错误处理
async function fetchData() {
  const data = await fetch('https://api.example.com/data');
  return data.json();
}

// 解决方案:使用 try-catch
async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    return data;
  } catch (error) {
    console.log('获取数据失败:', error);
    throw error;
  }
}

// 解决方案:使用 .catch()
async function fetchData() {
  const response = await fetch('https://api.example.com/data');
  return response.json();
}

fetchData().catch(error => {
  console.log('获取数据失败:', error);
});

问题 3:对象解构的默认值问题

// 问题代码
const { name, age = 0 } = null;
// TypeError: Cannot destructure property 'name' of 'null' as it is null.

// 解决方案:提供默认对象
const { name, age = 0 } = null || {};
console.log(name);  // undefined
console.log(age);   // 0

// 或使用可选链
const data = null;
const name = data?.name;
const age = data?.age || 0;
console.log(name);  // undefined
console.log(age);   // 0

问题 4:类中的方法绑定

// 问题代码
class Button {
  constructor() {
    this.count = 0;
  }
  
  handleClick() {
    this.count++;
    console.log(`点击次数:${this.count}`);
  }
}

const button = new Button();
const handler = button.handleClick;
handler();  // undefined(this 指向丢失)

// 解决方案 1:使用箭头函数
class Button {
  constructor() {
    this.count = 0;
  }
  
  handleClick = () => {
    this.count++;
    console.log(`点击次数:${this.count}`);
  };
}

// 解决方案 2:在构造函数中绑定
class Button {
  constructor() {
    this.count = 0;
    this.handleClick = this.handleClick.bind(this);
  }
  
  handleClick() {
    this.count++;
    console.log(`点击次数:${this.count}`);
  };
}

总结

本教程详细介绍了 JavaScript ES6+ 的新特性,包括 let/const、箭头函数、模板字符串、解构赋值、Promise、async/await、类和模块化等重要内容。这些新特性极大地提升了 JavaScript 的开发效率和代码质量。

通过本集的学习,您应该能够:

  1. 使用 let 和 const 声明变量
  2. 使用箭头函数简化函数定义
  3. 使用模板字符串进行字符串拼接
  4. 使用解构赋值提取数据
  5. 使用 Promise 和 async/await 处理异步操作
  6. 使用类进行面向对象编程
  7. 使用模块化组织代码
  8. 避免常见的 ES6+ 使用错误

在下一集中,我们将学习 Node.js 的模块系统,这是 Node.js 编程的核心概念之一。继续加油,您的现代 JavaScript 技能正在不断提升!

« 上一篇 JavaScript 数组与对象 下一篇 » Node.js 模块系统