JavaScript与ES6新特性
ES6(ECMAScript 2015)是JavaScript语言的一次重大更新,引入了大量新特性,使JavaScript更加强大和易用。本章将系统讲解ES6及后续版本的核心新特性。
一、变量声明
1.1 let 与 const
核心概念:let 和 const
为什么需要let和const?
ES5中只有var声明变量,存在变量提升、无块级作用域等问题。let和const的引入解决了这些问题,使变量声明更加安全和可控。
let、const、var 对比:
| 特性 | var | let | const |
| 作用域 | 函数作用域 | 块级作用域 | 块级作用域 |
| 变量提升 | 是(初始化为undefined) | 否(暂时性死区) | 否(暂时性死区) |
| 重复声明 | 允许 | 不允许 | 不允许 |
| 重新赋值 | 允许 | 允许 | 不允许 |
| 全局变量挂载 | 挂载到window | 不挂载 | 不挂载 |
块级作用域
if (true) {
var x = 10;
}
console.log(x);
if (true) {
let y = 20;
}
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log("var:" + i), 100);
}
for (let j = 0; j < 3; j++) {
setTimeout(() => console.log("let:" + j), 100);
}
暂时性死区(TDZ)
{
let x = 10;
console.log(x);
}
const PI = 3.14159;
const obj = { name: "张三" };
obj.name = "李四";
const frozenObj = Object.freeze({ value: 100 });
最佳实践:
默认使用 const,只有需要重新赋值时才用 let
避免使用 var
const 声明的对象,如需不可变,使用 Object.freeze()
二、模板字符串
核心概念:模板字符串
什么是模板字符串?
模板字符串使用反引号(`)包裹,支持插值表达式、多行字符串和标签模板,比传统字符串拼接更简洁、更强大。
插值与多行字符串
const name = "张三";
const age = 25;
const oldWay = "姓名:" + name + ",年龄:" + age;
const newWay = `姓名:${name},年龄:${age}`;
const a = 10;
const b = 20;
console.log(`${a} + ${b} = ${a + b}`);
const html = `
<div class="card">
<h2>${name}</h2>
<p>年龄:${age}岁</p>
</div>
`;
const items = ["苹果", "香蕉", "橙子"];
const list = `<ul>
${items.map(item => `<li>${item}</li>`).join("\n ")}
</ul>`;
标签模板
function tag(strings, ...values) {
console.log(strings);
console.log(values);
return strings.reduce((result, str, i) =>
result + str + (values[i] !== undefined ? values[i] : ""), "");
}
const name = "张三";
const age = 25;
tag`你好,${name}!你今年${age}岁了。`;
function safeHtml(strings, ...values) {
const escape = (str) => String(str)
.replace(/&/g, "&")
.replace(/, ">")
.replace(/"/g, """);
return strings.reduce((result, str, i) =>
result + str + (values[i] !== undefined ? escape(values[i]) : ""), "");
}
const userInput = '<script>alert("XSS")</script>';
const safe = safeHtml`<div>${userInput}</div>`;
三、函数增强
3.1 箭头函数
核心概念:箭头函数
箭头函数的特点:
语法更简洁
没有自己的this,继承外层作用域的this
没有arguments对象
不能作为构造函数(不能用new调用)
没有prototype属性
const add1 = function(a, b) {
return a + b;
};
const add2 = (a, b) => {
return a + b;
};
const add3 = (a, b) => a + b;
const double = x => x * 2;
const createPerson = (name, age) => ({ name, age });
this 绑定
const obj1 = {
name: "张三",
greet: function() {
setTimeout(function() {
console.log(this.name);
}, 100);
}
};
const obj2 = {
name: "张三",
greet: function() {
setTimeout(() => {
console.log(this.name);
}, 100);
}
};
const patient = {
name: "李四",
vitals: [120, 80, 72],
analyze: function() {
this.vitals.forEach((v, i) => {
console.log(`${this.name}的第${i+1}项生命体征:${v}`);
});
}
};
3.2 函数默认参数
function greet1(name) {
name = name || "访客";
return `你好,${name}!`;
}
function greet2(name = "访客") {
return `你好,${name}!`;
}
greet2();
greet2("张三");
greet2(""); (空字符串不会被替换)
function getConfig(url, timeout = fetchTimeout(), headers = {}) {
}
function fn(a, b = 2, c) {
console.log(a, b, c);
}
fn(1);
fn(1, undefined, 3);
3.3 剩余参数与展开运算符
function sum(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
sum(1, 2, 3, 4, 5);
function log(prefix, ...messages) {
messages.forEach(msg => console.log(prefix + msg));
}
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const merged = [...arr1, ...arr2];
const copied = [...arr1];
Math.max(...arr1);
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, ...obj1 };
const baseInfo = { hospital: "中心医院", department: "内科" };
const patientInfo = { ...baseInfo, name: "王五", age: 45 };
四、解构赋值
核心概念:解构赋值
什么是解构赋值?
解构赋值是一种从数组或对象中提取值并赋给变量的语法。它可以让代码更简洁,特别是在处理复杂数据结构时。
数组解构
const [a, b] = [1, 2];
console.log(a, b);
const [x, , y] = [1, 2, 3];
console.log(x, y);
const [first, ...rest] = [1, 2, 3, 4, 5];
console.log(first);
console.log(rest);
const [m = 10, n = 20] = [undefined, 5];
console.log(m, n);
let p = 1, q = 2;
[p, q] = [q, p];
console.log(p, q);
对象解构
const { name, age } = { name: "张三", age: 25 };
console.log(name, age);
const { name: userName, age: userAge } = { name: "李四", age: 30 };
console.log(userName, userAge);
const { x = 100, y = 200 } = { x: 10 };
console.log(x, y);
const { a, ...others } = { a: 1, b: 2, c: 3 };
console.log(a);
console.log(others);
const patient = {
id: "P001",
name: "王五",
vitals: {
bloodPressure: { systolic: 120, diastolic: 80 },
heartRate: 72
}
};
const { id, name: patientName, vitals: { bloodPressure: { systolic } } } = patient;
console.log(patientName, systolic);
嵌套解构与默认值
const [[a1, a2], [b1, b2]] = [[1, 2], [3, 4]];
const data = {
user: {
profile: {
name: "张三",
settings: {
theme: "dark"
}
}
}
};
const { user: { profile: { name, settings: { theme = "light" } = {} } = {} } = {} } = data;
function createUser({ name, age = 0, role = "user" } = {}) {
return { name, age, role };
}
createUser({ name: "张三" });
createUser();
五、对象字面量增强
const name = "张三";
const age = 25;
const user1 = { name: name, age: age };
const user2 = { name, age };
const obj = {
sayHello: function() {
return "Hello";
},
greet() {
return "Hi";
}
};
const key = "dynamic";
const obj2 = {
[key]: "value",
[`prefix_${key}`]: "another value"
};
const createPatient = (id, name, ward) => ({
id,
name,
ward,
[`ward_${ward}`]: true,
getInfo() {
return `患者${this.name},编号${this.id}`;
}
});
六、数组与对象扩展
6.1 数组方法
ES6+ 新增数组方法:
| 方法 | 说明 | 返回值 |
| forEach() | 遍历数组 | undefined |
| map() | 映射转换 | 新数组 |
| filter() | 过滤元素 | 新数组 |
| reduce() | 累积计算 | 累积值 |
| find() | 查找元素 | 元素或undefined |
| findIndex() | 查找索引 | 索引或-1 |
| some() | 是否存在满足条件的元素 | boolean |
| every() | 是否所有元素都满足条件 | boolean |
| includes() | 是否包含某值 | boolean |
| Array.from() | 从类数组创建数组 | 新数组 |
| Array.of() | 创建数组 | 新数组 |
const patients = [
{ id: 1, name: "张三", age: 45, department: "内科" },
{ id: 2, name: "李四", age: 32, department: "外科" },
{ id: 3, name: "王五", age: 58, department: "内科" }
];
patients.forEach(p => console.log(p.name));
const names = patients.map(p => p.name);
const internalMedicine = patients.filter(p => p.department === "内科");
const avgAge = patients.reduce((sum, p) => sum + p.age, 0) / patients.length;
const patient = patients.find(p => p.id === 2);
const hasElderly = patients.some(p => p.age >= 60);
const allAdults = patients.every(p => p.age >= 18);
const arr = Array.from("hello");
const nums = Array.from({ length: 5 }, (_, i) => i + 1);
6.2 对象方法
const patient = {
id: "P001",
name: "张三",
age: 45
};
Object.keys(patient);
Object.values(patient);
Object.entries(patient);
const merged = Object.assign({}, patient, { department: "内科" });
const obj = Object.fromEntries([["a", 1], ["b", 2]]);
七、Symbol 类型
核心概念:Symbol
什么是Symbol?
Symbol是ES6引入的一种新的原始数据类型,表示独一无二的值。每个Symbol值都是唯一的,主要用于创建对象的私有属性和内置行为。
const sym1 = Symbol();
const sym2 = Symbol("description");
console.log(sym1 === sym2);
const ID = Symbol("id");
const user = {
name: "张三",
[ID]: 12345
};
Object.keys(user);
user[ID];
const globalSym1 = Symbol.for("app.id");
const globalSym2 = Symbol.for("app.id");
console.log(globalSym1 === globalSym2);
八、Set 与 Map
8.1 Set
const set = new Set([1, 2, 3, 2, 1]);
console.log(set);
set.add(4);
set.delete(2);
set.has(1);
set.size;
set.clear();
const arr = [1, 2, 2, 3, 3, 3];
const unique = [...new Set(arr)];
for (const item of set) {
console.log(item);
}
8.2 Map
const map = new Map();
map.set("name", "张三");
map.set("age", 25);
map.get("name");
map.has("name");
map.delete("age");
map.size;
const objKey = { id: 1 };
map.set(objKey, "对象作为键");
for (const [key, value] of map) {
console.log(key, value);
}
8.3 WeakSet 与 WeakMap
const weakMap = new WeakMap();
let key = { id: 1 };
weakMap.set(key, "私有数据");
weakMap.get(key);
key = null;
const domData = new WeakMap();
const element = document.getElementById("myElement");
domData.set(element, { clickCount: 0 });
WeakSet 与 WeakMap 的特点:
只能存储对象作为键/值
弱引用:不影响垃圾回收
不可遍历(没有size属性、forEach等方法)
适用于缓存、私有数据存储等场景
九、迭代器与生成器
9.1 迭代器(Iterator)
核心概念:迭代器
什么是迭代器?
迭代器是一种对象,它提供了一种统一的方式来遍历集合中的元素。任何具有 Symbol.iterator 属性的对象都是可迭代的。
const iterableObj = {
data: [1, 2, 3],
[Symbol.iterator]() {
let index = 0;
const data = this.data;
return {
next() {
if (index < data.length) {
return { value: data[index++], done: false };
}
return { done: true };
}
};
}
};
for (const item of iterableObj) {
console.log(item);
}
const iterator = iterableObj[Symbol.iterator]();
console.log(iterator.next());
console.log(iterator.next());
9.2 for...of 循环
const arr = ["a", "b", "c"];
for (const item of arr) {
console.log(item);
}
for (const char of "hello") {
console.log(char);
}
const map = new Map([["a", 1], ["b", 2]]);
for (const [key, value] of map) {
console.log(key, value);
}
9.3 生成器函数
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = numberGenerator();
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());
function* idGenerator() {
let id = 1;
while (true) {
yield id++;
}
}
const ids = idGenerator();
console.log(ids.next().value);
console.log(ids.next().value);
function* fetchData() {
try {
const user = yield fetch("/api/user");
const posts = yield fetch(`/api/posts/${user.id}`);
return posts;
} catch (e) {
console.error(e);
}
}
十、类(class)
核心概念:类
ES6 类的本质:
ES6 的 class 本质上是构造函数的语法糖,它让对象原型的写法更加清晰、更像面向对象编程的语法。类不会提升,必须先定义后使用。
10.1 类的声明与实例化
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
return `你好,我是${this.name},今年${this.age}岁`;
}
static create(name, age) {
return new Person(name, age);
}
}
const person = new Person("张三", 25);
console.log(person.greet());
console.log(Person.create("李四", 30).greet());
10.2 类的继承
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name}发出声音`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
speak() {
console.log(`${this.name}汪汪叫`);
}
fetch() {
console.log(`${this.name}去捡球`);
}
}
const dog = new Dog("旺财", "金毛");
dog.speak();
dog.fetch();
class Patient extends Person {
constructor(name, age, medicalRecord) {
super(name, age);
this.medicalRecord = medicalRecord;
this.admissions = [];
}
admit(department) {
this.admissions.push({
department,
date: new Date()
});
}
}
10.3 私有属性与方法
class BankAccount {
#balance = 0;
#pin;
constructor(initialBalance, pin) {
this.#balance = initialBalance;
this.#pin = pin;
}
#validatePin(inputPin) {
return inputPin === this.#pin;
}
deposit(amount) {
if (amount > 0) {
this.#balance += amount;
}
}
withdraw(amount, pin) {
if (this.#validatePin(pin) && amount <= this.#balance) {
this.#balance -= amount;
return amount;
}
return 0;
}
getBalance() {
return this.#balance;
}
}
const account = new BankAccount(1000, "1234");
console.log(account.getBalance());
十一、模块化
核心概念:ES6 模块
模块化的优势:
代码分割:将代码拆分成独立的模块,便于维护
命名空间:避免全局变量污染
依赖管理:明确模块之间的依赖关系
按需加载:提高页面加载性能
11.1 导出(export)
export const PI = 3.14159;
export function sum(a, b) {
return a + b;
}
export class Calculator {
add(a, b) { return a + b; }
subtract(a, b) { return a - b; }
}
const name = "utils";
const version = "1.0.0";
export { name, version };
export { sum as addNumbers };
export default class Patient {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
11.2 导入(import)
import Patient from "./utils.js";
import { PI, sum, Calculator } from "./utils.js";
import { sum as add } from "./utils.js";
import * as Utils from "./utils.js";
Utils.PI;
import Patient, { PI, sum } from "./utils.js";
import("./utils.js").then(module => {
console.log(module.PI);
});
模块化注意事项:
ES6 模块默认运行在严格模式下
模块中的变量是局部的,不会污染全局作用域
import 是静态的,必须在模块顶层使用(动态导入除外)
导出的值是"活绑定",导入方可以获取更新后的值
十二、Promise 基础
核心概念:Promise
什么是Promise?
Promise是ES6引入的异步编程解决方案,它代表一个异步操作的最终完成(或失败)及其结果值。Promise有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败),状态一旦改变就不会再变。
12.1 Promise 的创建与状态
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const success = true;
if (success) {
resolve("操作成功!");
} else {
reject("操作失败!");
}
}, 1000);
});
12.2 then、catch、finally
promise
.then(result => {
console.log("成功:" + result);
return "处理后的结果";
})
.then(data => {
console.log(data);
});
promise
.then(result => {
})
.catch(error => {
console.error("错误:" + error);
});
promise
.then(result => console.log(result))
.catch(error => console.error(error))
.finally(() => {
console.log("操作完成(无论成功或失败)");
});
12.3 错误处理
function fetchPatientData(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id > 0) {
resolve({
id,
name: "张三",
age: 45,
department: "内科"
});
} else {
reject(new Error("无效的患者ID"));
}
}, 500);
});
}
fetchPatientData(1)
.then(patient => {
console.log("患者信息:", patient);
return fetchPatientData(2);
})
.then(patient2 => {
console.log("第二位患者:", patient2);
})
.catch(error => {
console.error("获取失败:", error.message);
});
Promise.all([
fetchPatientData(1),
fetchPatientData(2),
fetchPatientData(3)
]).then(patients => {
console.log("所有患者:", patients);
}).catch(error => {
console.error("至少一个请求失败");
});
Promise.race([
fetchPatientData(1),
new Promise((_, reject) =>
setTimeout(() => reject(new Error("请求超时")), 300)
)
]);
十三、总结
ES6+ 核心新特性回顾
| 类别 | 特性 | 主要用途 |
| 变量声明 | let、const | 块级作用域、常量声明 |
| 字符串 | 模板字符串 | 字符串插值、多行文本 |
| 函数 | 箭头函数、默认参数、rest/spread | 简洁语法、this绑定、灵活参数 |
| 解构 | 数组解构、对象解构 | 快速提取数据 |
| 对象 | 属性简写、计算属性 | 简化对象定义 |
| 数据结构 | Symbol、Set、Map | 唯一值、集合、键值对 |
| 流程控制 | 迭代器、生成器 | 自定义遍历、惰性计算 |
| 面向对象 | class、extends | 类定义、继承 |
| 模块化 | export、import | 代码组织、依赖管理 |
| 异步编程 | Promise | 异步操作、链式调用 |
学习建议:
ES6+ 特性是现代 JavaScript 开发的基础,务必熟练掌握
优先使用 const/let 替代 var
善用解构和展开运算符简化代码
理解 Promise 是学习 async/await 的基础
模块化是构建大型应用的必备技能
十四、学习资源
ECMAScript 6 入门 - 阮一峰
MDN JavaScript 文档
现代 JavaScript 教程
ECMAScript 规范(需要梯子访问)