01-ES6新特性概览
分类:02-ES6+现代特性
发布于:
阅读时间:128 分钟
ES6+新特性概览
📋 学习目标
- 掌握ES6(ECMAScript 2015)的核心特性
- 理解let、const、模板字符串、解构赋值等语法糖
- 学会使用Symbol、Set、Map等新的数据结构
- 掌握Promise、迭代器、生成器等异步编程特性
- 了解ES6+后续版本的新特性
🎯 块级作用域与变量声明
1. let 和 const
// var 的问题:变量提升和函数作用域
function varExample() {
console.log(a); // undefined (变量提升)
if (true) {
var a = 1;
}
console.log(a); // 1 (函数作用域)
}
// let:块级作用域,无变量提升
function letExample() {
// console.log(b); // ReferenceError: Cannot access 'b' before initialization
if (true) {
let b = 1;
console.log(b); // 1
}
// console.log(b); // ReferenceError: b is not defined (块级作用域)
}
// const:块级作用域,必须初始化,不可重新赋值
const PI = 3.14159;
// PI = 3.14; // TypeError: Assignment to constant variable
// 对象和数组的const属性可以修改
const obj = { name: 'Alice' };
obj.name = 'Bob'; // ✅ 可以修改属性
// obj = {}; // ❌ 不能重新赋值
const arr = [1, 2, 3];
arr.push(4); // ✅ 可以修改数组内容
// arr = []; // ❌ 不能重新赋值
2. 暂时性死区(Temporal Dead Zone)
// 暂时性死区:在声明之前不能访问
{
// TDZ开始
// console.log(myVar); // ReferenceError
let myVar = 'hello';
// TDZ结束
console.log(myVar); // 'hello'
}
// 不同块级作用域中的let
let x = 1;
{
let x = 2;
console.log(x); // 2
}
console.log(x); // 1
🔧 解构赋值
1. 数组解构
// 基础数组解构
const arr = [1, 2, 3, 4, 5];
const [first, second, third] = arr;
console.log(first, second, third); // 1 2 3
// 跳过某些元素
const [a, , c] = arr;
console.log(a, c); // 1 3
// 剩余元素
const [head, ...tail] = arr;
console.log(head); // 1
console.log(tail); // [2, 3, 4, 5]
// 默认值
const [x = 1, y = 2] = [10];
console.log(x, y); // 10 2
// 交换变量
let m = 1, n = 2;
[m, n] = [n, m];
console.log(m, n); // 2 1
// 函数返回值解构
function getUser() {
return ['Alice', 25, 'Developer'];
}
const [name, age, profession] = getUser();
2. 对象解构
// 基础对象解构
const person = {
name: 'Alice',
age: 25,
city: 'New York'
};
const { name, age } = person;
console.log(name, age); // Alice 25
// 重命名变量
const { name: userName, age: userAge } = person;
console.log(userName, userAge); // Alice 25
// 默认值
const { country = 'USA' } = person;
console.log(country); // USA
// 嵌套对象解构
const user = {
profile: {
name: 'Bob',
contact: {
email: 'bob@example.com',
phone: '123-456-7890'
}
}
};
const {
profile: {
name: profileName,
contact: { email }
}
} = user;
console.log(profileName, email); // Bob bob@example.com
// 函数参数解构
function greet({ name, age }) {
console.log(`Hello ${name}, you are ${age} years old`);
}
greet(person); // Hello Alice, you are 25 years old
3. 复杂解构模式
// 数组+对象混合解构
const data = {
users: [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
],
meta: { total: 2, page: 1 }
};
const {
users: [
{ id: firstUserId },
{ id: secondUserId }
],
meta: { total }
} = data;
console.log(firstUserId, secondUserId, total); // 1 2 2
// 解构赋值的应用
function processApiData({ data: { items, pagination } }) {
console.log(`Found ${items.length} items`);
console.log(`Page ${pagination.current} of ${pagination.total}`);
}
📝 模板字符串
1. 基础用法
// 传统字符串拼接
const name = 'Alice';
const age = 25;
const message = 'Hello, my name is ' + name + ' and I am ' + age + ' years old.';
// 模板字符串
const templateMessage = `Hello, my name is ${name} and I am ${age} years old.`;
console.log(templateMessage);
// 多行字符串
const multiline = `
<div>
<h1>${name}</h1>
<p>Age: ${age}</p>
</div>
`;
console.log(multiline);
2. 高级模板字符串
// 表达式计算
const price = 19.99;
const quantity = 3;
const total = `Total: $${(price * quantity).toFixed(2)}`;
// 条件表达式
const isAdmin = true;
const greeting = `Welcome ${isAdmin ? 'Admin' : 'User'}`;
// 函数调用
function formatName(first, last) {
return `${last}, ${first}`;
}
const fullName = formatName('John', 'Doe');
// 嵌套模板字符串
const user = { name: 'Alice', role: 'admin' };
const userCard = `
<div class="user-card ${user.role}">
<h2>${user.name}</h2>
<span>Role: ${user.role.toUpperCase()}</span>
</div>
`;
3. 标签模板函数
// 基础标签模板
function highlight(strings, ...values) {
return strings.reduce((result, str, i) => {
const value = values[i] ? `<mark>${values[i]}</mark>` : '';
return result + str + value;
}, '');
}
const name = 'Alice';
const age = 25;
const highlighted = highlight`Name: ${name}, Age: ${age}`;
console.log(highlighted); // Name: <mark>Alice</mark>, Age: <mark>25</mark>
// 实际应用:SQL查询构建
function sql(strings, ...values) {
return strings.reduce((query, str, i) => {
const value = values[i] !== undefined ? `'${values[i]}'` : '';
return query + str + value;
}, '');
}
const tableName = 'users';
const userId = 123;
const query = sql`SELECT * FROM ${tableName} WHERE id = ${userId}`;
console.log(query); // SELECT * FROM 'users' WHERE id = '123'
// HTML转义模板
function html(strings, ...values) {
return strings.reduce((result, str, i) => {
const value = values[i] ? String(values[i])
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>') : '';
return result + str + value;
}, '');
}
const userInput = '<script>alert("xss")</script>';
const safeHtml = html`User input: ${userInput}`;
console.log(safeHtml); // User input: <script>alert("xss")</script>
🔤 Symbol类型
1. Symbol基础
// 创建Symbol
const sym1 = Symbol();
const sym2 = Symbol('description');
const sym3 = Symbol('description');
console.log(sym2 === sym3); // false,每个Symbol都是唯一的
// Symbol作为对象属性
const obj = {};
const mySymbol = Symbol('my symbol');
obj[mySymbol] = 'secret value';
obj.normalProp = 'normal value';
console.log(obj[mySymbol]); // 'secret value'
console.log(obj.normalProp); // 'normal value'
// Symbol属性不会出现在for...in循环中
for (const key in obj) {
console.log(key); // 'normalProp' (Symbol属性被跳过)
}
// 获取Symbol属性
const symbolKeys = Object.getOwnPropertySymbols(obj);
console.log(symbolKeys); // [Symbol(my symbol)]
// 使用Reflect.ownKeys获取所有属性(包括Symbol)
const allKeys = Reflect.ownKeys(obj);
console.log(allKeys); // ['normalProp', Symbol(my symbol)]
2. 内置Symbol
// Symbol.iterator - 定义对象的迭代器
const myObject = {
data: [1, 2, 3],
[Symbol.iterator]() {
let index = 0;
return {
next: () => ({
value: this.data[index++],
done: index > this.data.length
})
};
}
};
for (const value of myObject) {
console.log(value); // 1, 2, 3
}
// Symbol.toStringTag - 自定义对象标签
class MyClass {
constructor(name) {
this.name = name;
}
get [Symbol.toStringTag]() {
return 'MyClass';
}
}
const instance = new MyClass('test');
console.log(instance.toString()); // [object MyClass]
// Symbol.hasInstance - 自定义instanceof行为
class MyArray {
static [Symbol.hasInstance](instance) {
return Array.isArray(instance);
}
}
console.log([] instanceof MyArray); // true
console.log({} instanceof MyArray); // false
3. Symbol的实际应用
// 私有属性模拟
class Person {
[#name](/tags/name) = Symbol('name'); // 私有Symbol属性
constructor(name) {
this[this.#name] = name;
}
getName() {
return this[this.#name];
}
}
const person = new Person('Alice');
console.log(person.getName()); // 'Alice'
// console.log(person[Symbol('name')]); // undefined,无法直接访问
// 常量定义
const COLORS = {
RED: Symbol('red'),
GREEN: Symbol('green'),
BLUE: Symbol('blue')
};
function setColor(color) {
if (color === COLORS.RED) {
console.log('Setting red color');
}
}
// 避免属性名冲突
const API_ENDPOINTS = {
[Symbol.for('api')]: 'https://api.example.com',
[Symbol.for('version')]: 'v1'
};
// 使用全局Symbol注册表
const globalSym1 = Symbol.for('global');
const globalSym2 = Symbol.for('global');
console.log(globalSym1 === globalSym2); // true
🏗️ 新的数据结构
1. Set集合
// 创建Set
const set1 = new Set();
const set2 = new Set([1, 2, 3, 4, 5]);
const set3 = new Set('hello'); // Set {'h', 'e', 'l', 'l', 'o'}
// 基本操作
set1.add(1);
set1.add(2);
set1.add(3);
set1.add(1); // 重复添加无效
console.log(set1.has(2)); // true
console.log(set1.has(4)); // false
set1.delete(2);
console.log(set1.has(2)); // false
set1.clear();
console.log(set1.size); // 0
// Set的遍历
const numbers = new Set([1, 2, 3, 4, 5]);
// for...of
for (const num of numbers) {
console.log(num);
}
// forEach
numbers.forEach((value, key, set) => {
console.log(value, key, set);
});
// Set转数组
const arr1 = [...numbers];
const arr2 = Array.from(numbers);
// 数组去重
const duplicates = [1, 2, 2, 3, 4, 4, 5];
const unique = [...new Set(duplicates)];
console.log(unique); // [1, 2, 3, 4, 5]
// 实际应用:交集、并集、差集
const setA = new Set([1, 2, 3, 4]);
const setB = new Set([3, 4, 5, 6]);
// 并集
const union = new Set([...setA, ...setB]); // {1, 2, 3, 4, 5, 6}
// 交集
const intersection = new Set([...setA].filter(x => setB.has(x))); // {3, 4}
// 差集
const difference = new Set([...setA].filter(x => !setB.has(x))); // {1, 2}
2. Map映射
// 创建Map
const map1 = new Map();
const map2 = new Map([
['name', 'Alice'],
['age', 25]
]);
// 基本操作
map1.set('key1', 'value1');
map1.set('key2', 'value2');
map1.set(1, 'number key');
map1.set({}, 'object key');
console.log(map1.get('key1')); // 'value1'
console.log(map1.has('key2')); // true
console.log(map1.size); // 4
map1.delete('key1');
console.log(map1.has('key1')); // false
// Map的遍历
const userMap = new Map([
['name', 'Bob'],
['age', 30],
['city', 'New York']
]);
// 遍历键
for (const key of userMap.keys()) {
console.log(key); // name, age, city
}
// 遍历值
for (const value of userMap.values()) {
console.log(value); // Bob, 30, New York
}
// 遍历键值对
for (const [key, value] of userMap.entries()) {
console.log(`${key}: ${value}`);
}
// forEach
userMap.forEach((value, key) => {
console.log(`${key}: ${value}`);
});
// Map与Object的转换
const obj = { name: 'Alice', age: 25 };
const mapFromObj = new Map(Object.entries(obj));
const objFromMap = Object.fromEntries(mapFromObj);
// 实际应用:缓存
function memoize(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
const expensiveFunction = memoize((n) => {
console.log('Computing...');
return n * 2;
});
console.log(expensiveFunction(5)); // Computing... 10
console.log(expensiveFunction(5)); // 10 (从缓存获取)
3. WeakSet和WeakMap
// WeakSet:弱引用的Set
const weakSet = new WeakSet();
const obj1 = { name: 'Object 1' };
const obj2 = { name: 'Object 2' };
weakSet.add(obj1);
weakSet.add(obj2);
console.log(weakSet.has(obj1)); // true
weakSet.delete(obj1);
console.log(weakSet.has(obj1)); // false
// WeakSet只能存储对象,不能遍历
// weakSet.add(1); // TypeError
// WeakMap:弱引用的Map
const weakMap = new WeakMap();
const key1 = { id: 1 };
const key2 = { id: 2 };
weakMap.set(key1, 'value for key1');
weakMap.set(key2, 'value for key2');
console.log(weakMap.get(key1)); // 'value for key1'
// 实际应用:私有数据存储
class PrivateData {
constructor() {
this._data = new WeakMap();
}
set(obj, data) {
this._data.set(obj, data);
}
get(obj) {
return this._data.get(obj);
}
}
const privateData = new PrivateData();
const user = { name: 'Alice' };
privateData.set(user, { secret: 'private info' });
console.log(privateData.get(user)); // { secret: 'private info' }
🔄 迭代器和生成器
1. 迭代器(Iterator)
// 自定义迭代器
class Counter {
constructor(limit) {
this.limit = limit;
this.current = 0;
}
[Symbol.iterator]() {
return {
next: () => {
if (this.current < this.limit) {
return { value: this.current++, done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
}
const counter = new Counter(5);
for (const num of counter) {
console.log(num); // 0, 1, 2, 3, 4
}
// 使用迭代器协议
const iterator = counter[Symbol.iterator]();
console.log(iterator.next()); // { value: 0, done: false }
console.log(iterator.next()); // { value: 1, done: false }
// 可迭代对象
const iterable = {
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 };
} else {
return { value: undefined, done: true };
}
}
};
}
};
console.log([...iterable]); // [1, 2, 3]
2. 生成器(Generator)
// 基础生成器
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
return 'done';
}
const gen = numberGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: 'done', done: true }
// 无限序列生成器
function* fibonacci() {
let a = 0, b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
const fib = fibonacci();
console.log(fib.next().value); // 0
console.log(fib.next().value); // 1
console.log(fib.next().value); // 1
console.log(fib.next().value); // 2
// 带参数的生成器
function* range(start, end, step = 1) {
for (let i = start; i <= end; i += step) {
yield i;
}
}
console.log([...range(1, 5)]); // [1, 2, 3, 4, 5]
console.log([...range(0, 10, 2)]); // [0, 2, 4, 6, 8, 10]
// 生成器中的错误处理
function* errorGenerator() {
try {
yield 1;
throw new Error('Something went wrong');
yield 2;
} catch (error) {
console.log('Caught:', error.message);
yield 3;
}
}
const errorGen = errorGenerator();
console.log(errorGen.next().value); // 1
console.log(errorGen.next()); // Caught: Something went wrong
console.log(errorGen.next().value); // 3
3. 生成器的实际应用
// 异步任务管理器
function* taskManager() {
const tasks = [];
while (true) {
const action = yield;
if (action.type =<mark> 'add') {
tasks.push(action.task);
console.log(`Task added: ${action.task}`);
} else if (action.type </mark>= 'run') {
console.log('Running tasks...');
for (const task of tasks) {
console.log(`Executing: ${task}`);
yield task; // 可以在这里执行实际任务
}
} else if (action.type === 'stop') {
break;
}
}
}
const manager = taskManager();
manager.next(); // 启动生成器
manager.next({ type: 'add', task: 'Task 1' });
manager.next({ type: 'add', task: 'Task 2' });
manager.next({ type: 'run' });
// 数据流处理
function* dataProcessor(data) {
for (const item of data) {
// 转换数据
const processed = item * 2;
yield processed;
}
}
const numbers = [1, 2, 3, 4, 5];
const processedData = [...dataProcessor(numbers)];
console.log(processedData); // [2, 4, 6, 8, 10]
🔧 对象和数组的扩展
1. 对象扩展
// 属性简写
const name = 'Alice';
const age = 25;
const person = {
name, // 等同于 name: name
age, // 等同于 age: age
greet() { // 方法简写
return `Hello, I'm ${this.name}`;
}
};
console.log(person.greet()); // Hello, I'm Alice
// 计算属性名
const prop = 'dynamic';
const dynamicObj = {
[prop]: 'value',
[`${prop}Prop`]: 'another value'
};
console.log(dynamicObj.dynamic); // 'value'
console.log(dynamicObj.dynamicProp); // 'another value'
// Object.assign() - 对象合并
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
const merged = Object.assign({}, obj1, obj2);
console.log(merged); // { a: 1, b: 3, c: 4 }
// 展开运算符(ES2018)
const obj3 = { a: 1, b: 2 };
const obj4 = { ...obj3, c: 3 };
console.log(obj4); // { a: 1, b: 2, c: 3 }
// 对象方法扩展
const keys = Object.keys({ a: 1, b: 2 }); // ['a', 'b']
const values = Object.values({ a: 1, b: 2 }); // [1, 2]
const entries = Object.entries({ a: 1, b: 2 }); // [['a', 1], ['b', 2]]
// Object.fromEntries() - 从条目创建对象
const entries2 = [['name', 'Alice'], ['age', 25]];
const objFromEntries = Object.fromEntries(entries2);
console.log(objFromEntries); // { name: 'Alice', age: 25 }
2. 数组扩展
// 展开运算符
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]
// Array.from() - 从类数组创建数组
const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
const realArray = Array.from(arrayLike); // ['a', 'b', 'c']
// Array.of() - 创建数组
console.log(Array.of(1, 2, 3)); // [1, 2, 3]
console.log(new Array(1, 2, 3)); // [1, 2, 3]
console.log(new Array(3)); // [empty × 3] (可能不是期望的结果)
// find() 和 findIndex()
const numbers = [1, 2, 3, 4, 5];
const found = numbers.find(x => x > 3); // 4
const foundIndex = numbers.findIndex(x => x > 3); // 3
// includes()
console.log([1, 2, 3].includes(2)); // true
console.log([1, 2, 3].includes(4)); // false
// keys(), values(), entries()
const keys = numbers.keys(); // 迭代器
const values = numbers.values(); // 迭代器
const entries = numbers.entries(); // 迭代器
for (const [index, value] of entries) {
console.log(index, value); // 0 1, 1 2, 2 3, 3 4, 4 5
}
🔧 函数扩展
1. 箭头函数
// 基础箭头函数
const add = (a, b) => a + b;
console.log(add(1, 2)); // 3
// 单参数可以省略括号
const double = x => x * 2;
console.log(double(5)); // 10
// 多行箭头函数
const complex = (a, b) => {
const result = a + b;
return result * 2;
};
console.log(complex(2, 3)); // 10
// 箭头函数中的this
const obj = {
name: 'Object',
methods: {
// 普通函数:this指向调用时的对象
regularMethod: function() {
console.log(this.name); // 'methods'
},
// 箭头函数:this继承外部作用域
arrowMethod: () => {
console.log(this.name); // undefined (或全局对象)
}
}
};
obj.methods.regularMethod();
obj.methods.arrowMethod();
// 实际应用:回调函数
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 }
];
const names = users.map(user => user.name);
console.log(names); // ['Alice', 'Bob']
// 实际应用:事件处理器
class Component {
constructor() {
this.name = 'Component';
// 箭头函数保持this指向
this.handleClick = () => {
console.log(this.name);
};
}
render() {
return `
<button onclick="${this.handleClick}">
Click me
</button>
`;
}
}
2. 默认参数
// 基础默认参数
function greet(name = 'Guest', age = 18) {
return `Hello ${name}, you are ${age} years old`;
}
console.log(greet()); // Hello Guest, you are 18 years old
console.log(greet('Alice')); // Hello Alice, you are 18 years old
console.log(greet('Bob', 25)); // Hello Bob, you are 25 years old
// 解构与默认参数结合
function createUser({ name = 'Anonymous', age = 0, city = 'Unknown' } = {}) {
return { name, age, city };
}
console.log(createUser()); // { name: 'Anonymous', age: 0, city: 'Unknown' }
console.log(createUser({ name: 'Alice', age: 25 })); // { name: 'Alice', age: 25, city: 'Unknown' }
// 默认参数可以是表达式
function randomMultiplier(factor = Math.random()) {
return Math.floor(Math.random() * 10 * factor);
}
console.log(randomMultiplier());
console.log(randomMultiplier(2));
3. 剩余参数
// 基础剩余参数
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // 15
// 与命名参数结合
function greet(greeting, ...names) {
return `${greeting}, ${names.join(', ')}!`;
}
console.log(greet('Hello', 'Alice', 'Bob', 'Charlie')); // Hello, Alice, Bob, Charlie!
// 解构与剩余参数
const [first, ...rest] = [1, 2, 3, 4, 5];
console.log(first, rest); // 1 [2, 3, 4, 5]
// 实际应用:参数验证
function validateParams(required, ...optional) {
if (!required) {
throw new Error('Required parameter is missing');
}
console.log('Required:', required);
console.log('Optional:', optional);
}
🎯 ES6+后续版本特性
1. ES2017 (ES8) 新特性
// async/await
async function fetchData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
return data;
} catch (error) {
console.error('Error:', error);
}
}
// Object.values() 和 Object.entries()
const obj = { a: 1, b: 2, c: 3 };
console.log(Object.values(obj)); // [1, 2, 3]
console.log(Object.entries(obj)); // [['a', 1], ['b', 2], ['c', 3]]
// String.prototype.padStart() 和 padEnd()
const str = '5';
console.log(str.padStart(3, '0')); // '005'
console.log(str.padEnd(5, '*')); // '5****'
// Object.getOwnPropertyDescriptors()
const descriptors = Object.getOwnPropertyDescriptors(obj);
console.log(descriptors);
2. ES2018 (ES9) 新特性
// 对象展开运算符
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 }; // { a: 1, b: 2, c: 3 }
// Promise.prototype.finally()
Promise.resolve('success')
.then(result => console.log(result))
.catch(error => console.error(error))
.finally(() => console.log('Always executed'));
// 异步迭代器
async function processAsync() {
const asyncIterable = {
[Symbol.asyncIterator]() {
let i = 0;
return {
async next() {
if (i < 3) {
return { value: i++, done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
};
for await (const num of asyncIterable) {
console.log(num);
}
}
3. ES2019 (ES10) 新特性
// Array.prototype.flat() 和 flatMap()
const nested = [1, [2, 3], [4, [5, 6]]];
console.log(nested.flat()); // [1, 2, 3, 4, [5, 6]]
console.log(nested.flat(2)); // [1, 2, 3, 4, 5, 6]
const arr = [1, 2, 3];
console.log(arr.flatMap(x => [x, x * 2])); // [1, 2, 2, 4, 3, 6]
// Object.fromEntries()
const entries = [['a', 1], ['b', 2]];
const obj = Object.fromEntries(entries); // { a: 1, b: 2 }
// String.prototype.trimStart() 和 trimEnd()
const str = ' hello world ';
console.log(str.trimStart()); // 'hello world '
console.log(str.trimEnd()); // ' hello world'
// 可选的catch绑定
try {
throw new Error('Something went wrong');
} catch { // 不需要参数
console.log('An error occurred');
}
4. ES2020 (ES11) 新特性
// BigInt
const bigInt = 123456789012345678901234567890n;
console.log(bigInt + 1n); // 123456789012345678901234567891n
// 空值合并运算符 (??)
const foo = null ?? 'default'; // 'default'
const bar = undefined ?? 'default'; // 'default'
const baz = 0 ?? 'default'; // 0
// 可选链操作符 (?.)
const user = {
name: 'Alice',
address: {
city: 'New York'
}
};
console.log(user.address?.city); // 'New York'
console.log(user.contact?.email); // undefined
console.log(user.contact?.email ?? 'No email'); // 'No email'
// Promise.allSettled()
const promises = [
Promise.resolve(1),
Promise.reject('error'),
Promise.resolve(3)
];
Promise.allSettled(promises).then(results => {
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log('Success:', result.value);
} else {
console.log('Error:', result.reason);
}
});
});
5. ES2021 (ES12) 新特性
// String.prototype.replaceAll()
const message = 'Hello world, hello world';
const replaced = message.replaceAll('world', 'universe');
console.log(replaced); // Hello universe, hello universe
// 逻辑赋值运算符
let x = { a: 1 };
x.a ||= 2; // 等同于 x.a = x.a || 2
x.a &&= 3; // 等同于 x.a = x.a && 3
x.a ??= 4; // 等同于 x.a = x.a ?? 4
// 数字分隔符
const billion = 1_000_000_000;
const bytes = 0b1010_0001_1100_0010;
console.log(billion, bytes);
// Promise.any()
const promise1 = Promise.reject('error1');
const promise2 = Promise.resolve('success');
const promise3 = Promise.reject('error3');
Promise.any([promise1, promise2, promise3])
.then(result => console.log(result)) // 'success'
.catch(error => console.log(error)); // AggregateError if all reject
📝 最佳实践
1. 变量声明选择
// 优先级:const > let > var
const API_URL = 'https://api.example.com'; // 常量
let currentPage = 1; // 需要重新赋值的变量
// 避免使用var
2. 解构赋值最佳实践
// 函数参数解构
function processUser({ name, age, email }) {
// 使用解构,代码更清晰
}
// 默认值解构
const { name = 'Anonymous', settings = {} } = user;
// 重命名解构
const { name: userName, age: userAge } = user;
3. 模板字符串使用
// 复杂模板考虑使用函数
function renderTemplate(data) {
return `
<div class="card">
<h2>${data.title}</h2>
<p>${data.description}</p>
</div>
`;
}
// 避免在模板字符串中写复杂逻辑
// ❌ 不推荐
const bad = `Price: ${(price * quantity * discount).toFixed(2)}`;
// ✅ 推荐
const calculateTotal = () => (price * quantity * discount).toFixed(2);
const good = `Price: ${calculateTotal()}`;
🎯 小结
- 掌握了ES6的核心特性:let/const、解构赋值、模板字符串
- 学会了Symbol、Set、Map等新的数据结构
- 理解了迭代器、生成器等高级特性
- 了解了ES6+后续版本的新特性
- 掌握了现代JavaScript开发的最佳实践
下一步学习:解构赋值与扩展运算符