JavaScript ES6+ 新特性
章节介绍
ES6(ECMAScript 2015)是 JavaScript 的一个重要版本更新,引入了许多新特性和语法糖,极大地提升了开发效率和代码可读性。本教程将详细介绍 ES6+ 的核心新特性,帮助您编写更现代、更优雅的 JavaScript 代码。
核心知识点
let 和 const
ES6 引入了 let 和 const 关键字,用于声明块级作用域的变量。
// 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('')); // elloPromise
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+ 使用建议
- 优先使用 const:只有在需要重新赋值时才使用 let
- 使用箭头函数:特别是在回调函数中
- 使用解构赋值:提高代码可读性
- 使用模板字符串:比字符串拼接更清晰
- 使用 async/await:比 Promise 链更易读
兼容性考虑
- 检查浏览器支持:使用 Babel 转译不支持的特性
- 使用 Polyfill:为不支持新特性的环境提供替代实现
- 渐进增强:在新特性不可用时提供降级方案
性能优化
- 避免过度使用展开运算符:大对象展开可能影响性能
- 合理使用解构:频繁解构可能影响性能
- 使用适当的数据结构: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 的开发效率和代码质量。
通过本集的学习,您应该能够:
- 使用 let 和 const 声明变量
- 使用箭头函数简化函数定义
- 使用模板字符串进行字符串拼接
- 使用解构赋值提取数据
- 使用 Promise 和 async/await 处理异步操作
- 使用类进行面向对象编程
- 使用模块化组织代码
- 避免常见的 ES6+ 使用错误
在下一集中,我们将学习 Node.js 的模块系统,这是 Node.js 编程的核心概念之一。继续加油,您的现代 JavaScript 技能正在不断提升!