04-ES6+代码示例集
分类:02-ES6+现代特性
发布于:
阅读时间:112 分钟
ES6+代码示例集
📋 学习目标
- 通过实际代码示例理解ES6+特性的使用
- 掌握Promise和异步编程的实际应用
- 学会使用生成器函数和迭代器
- 理解对象操作和数组处理的高级技巧
- 掌握正则表达式的新特性
- 了解ES2015-ES2021的重要功能
🔄 Promise与异步编程
1. Promise基础使用
// 基础Promise示例
let pro = new Promise(function(resolve, reject) {
setTimeout(() => {
resolve('操作成功');
}, 1000);
});
pro.then((res) => {
console.log("成功:", res);
}).catch((err) => {
console.log("失败:", err);
});
// Promise封装Ajax请求
function ajax(url) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open("get", url, true);
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.responseText);
} else {
reject(new Error(`请求失败: ${xhr.status}`));
}
}
};
});
}
// 使用Promise的Ajax
ajax("https://api.example.com/data")
.then(res => {
console.log("数据获取成功:", res);
return JSON.parse(res);
})
.then(data => {
console.log("解析后的数据:", data);
})
.catch(err => {
console.error("请求错误:", err);
});
// Promise.finally示例
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("data-1111");
}, 1000);
});
}
fetchData()
.then(data => {
console.log(data);
})
.catch(err => {
console.error("错误:", err);
})
.finally(() => {
console.log("操作完成,无论成功失败");
// 隐藏加载动画等
});
2. Promise.allSettled
// ES11新增的Promise.allSettled
// 返回一个在所有给定的promise都已经fulfilled或rejected的promise
function fetchMultipleData() {
const requests = [
fetch('/api/users'),
fetch('/api/products'),
fetch('/api/orders')
];
return Promise.allSettled(requests);
}
fetchMultipleData().then(results => {
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`请求${index + 1}成功:`, result.value);
} else {
console.error(`请求${index + 1}失败:`, result.reason);
}
});
});
// 对比Promise.all和Promise.allSettled
const promises = [
Promise.resolve('成功1'),
Promise.reject('失败1'),
Promise.resolve('成功2')
];
// Promise.all - 有一个失败就立即失败
Promise.all(promises)
.catch(error => {
console.log('Promise.all 失败:', error); // 立即失败
});
// Promise.allSettled - 等待所有完成
Promise.allSettled(promises)
.then(results => {
console.log('Promise.allSettled 结果:', results);
// [
// { status: 'fulfilled', value: '成功1' },
// { status: 'rejected', reason: '失败1' },
// { status: 'fulfilled', value: '成功2' }
// ]
});
⚡ 生成器函数与迭代器
1. 基础生成器函数
// 生成器函数基础语法
function* gen() {
let res = yield "aaa";
console.log("第一个请求的结果" + res);
let res2 = yield "bbb";
console.log("第二个请求的结果" + res2);
return "生成器结束";
}
// 手动执行生成器
let g = gen();
console.log(g.next()); // { value: "aaa", done: false }
console.log(g.next("第一个yield的结果")); // { value: "bbb", done: false }
console.log(g.next("第二个yield的结果")); // { value: "生成器结束", done: true }
// 自动执行生成器的函数
function AutoRun(gen) {
let g = gen();
function next(data) {
let res = g.next(data);
if (res.done) return res.value;
// 如果返回的是Promise,等待完成后继续
if (res.value && typeof res.value.then === 'function') {
res.value.then(function(data) {
next(data);
}).catch(function(error) {
g.throw(error);
});
} else {
next(res.value);
}
}
next();
}
// 使用AutoRun执行生成器
AutoRun(function*() {
try {
let data1 = yield ajax('/api/data1');
console.log('数据1:', data1);
let data2 = yield ajax('/api/data2');
console.log('数据2:', data2);
let data3 = yield ajax('/api/data3');
console.log('数据3:', data3);
} catch (error) {
console.error('请求失败:', error);
}
});
2. 异步迭代器
// ES10新增的异步迭代器
async function* asyncGen() {
yield new Promise(resolve => setTimeout(() => resolve('延迟1秒'), 1000));
yield new Promise(resolve => setTimeout(() => resolve('延迟2秒'), 2000));
yield new Promise(resolve => setTimeout(() => resolve('延迟3秒'), 3000));
}
// 使用for await...of遍历异步迭代器
async function testAsyncIteration() {
let g = asyncGen();
console.log("开始异步遍历:", Date.now());
for await (let item of g) {
console.log("当前时间:", Date.now());
console.log("获取到值:", item);
}
console.log("遍历结束:", Date.now());
}
testAsyncIteration();
// 同步迭代器示例
let arr = [1, 2, 3];
let i = arr[Symbol.iterator]();
console.log(i.next()); // { value: 1, done: false }
console.log(i.next()); // { value: 2, done: false }
console.log(i.next()); // { value: 3, done: false }
console.log(i.next()); // { value: undefined, done: true }
// for...of循环本质上就是使用迭代器
for (let item of arr) {
console.log(item); // 1, 2, 3
}
🎯 对象操作高级技巧
1. 对象方法和属性
// 对象扩展方法
let obj = {
name: "tzd",
age: 100
};
console.log(Object.keys(obj)); // ["name", "age"]
console.log(Object.values(obj)); // ["tzd", 100]
console.log(Object.entries(obj)); // [["name", "tzd"], ["age", 100]]
// 对象快速转化为Map
let m = new Map([["a", 1], ["b", 2]]);
let M = new Map(Object.entries(obj));
console.log(M); // Map(2) { 'name' => 'tzd', 'age' => 100 }
// 获取对象所有属性的描述符
console.log(Object.getOwnPropertyDescriptors(obj));
2. 属性描述符和深拷贝
// 对象属性描述符示例
let obj1 = {
name: "tzd",
location: {
province: "辽宁",
city: "大连"
},
get city() {
return this.location.city;
},
set city(value) {
this.location.city = value;
},
get name() {
console.log("name getter被调用");
return this._name;
},
set name(value) {
console.log("name setter被调用");
this._name = value;
}
};
// Object.assign浅拷贝
let shallowCopy = Object.assign({}, obj1);
console.log("浅拷贝:", shallowCopy);
// 使用属性描述符进行深拷贝
let obj2 = {};
Object.defineProperties(obj2, Object.getOwnPropertyDescriptors(obj1));
console.log("深拷贝:", obj2);
// 验证深拷贝
console.log("原对象location =<mark> 拷贝对象location:",
obj1.location </mark>= obj2.location); // false
3. 对象的剩余参数和扩展运算符
// ES9新增:对象的剩余参数rest与扩展运算符spread
let obj3 = {
name: "tzd",
age: 13,
location: "北京",
job: "developer"
};
// 对象解构和剩余参数
let { name, ...other } = obj3;
console.log(name); // "tzd"
console.log(other); // { age: 13, location: "北京", job: "developer" }
// 扩展运算符合并对象
let defaults = {
method: "GET",
headers: {
"Content-Type": "application/json"
},
timeout: 5000
};
let userOptions = {
url: "/api/data",
method: "POST",
timeout: 10000
};
let finalOptions = {
...defaults,
...userOptions
};
console.log(finalOptions);
// {
// url: "/api/data",
// method: "POST",
// headers: { "Content-Type": "application/json" },
// timeout: 10000
// }
// Ajax函数使用默认参数和扩展运算符
function ajax(options) {
let defaultOptions = {
method: "GET",
async: true,
timeout: 5000,
headers: {}
};
// 合并默认参数和用户参数
options = { ...defaultOptions, ...options };
console.log("最终配置:", options);
}
ajax({
url: "/api",
method: "POST",
timeout: 3000
});
🔤 字符串处理新特性
1. 字符串填充方法
// ES8新增:padStart()和padEnd()
// 方法可以使得字符串达到固定长度,有两个参数:字符串目标长度和填充内容
let str = "5";
// padStart:在字符串开头填充
console.log(str.padStart(2, "0")); // "05"
console.log(str.padStart(4, "x")); // "xxx5"
console.log("abc".padStart(6, "0")); // "000abc"
// padEnd:在字符串末尾填充
console.log(str.padEnd(2, "0")); // "50"
console.log(str.padEnd(4, "x")); // "5xxx"
console.log("abc".padEnd(6, "0")); // "abc000"
// 实际应用:生成月份列表
let monthList = [];
for (let i = 1; i <= 12; i++) {
// 转化为字符串并填充到2位
monthList.push(String(i).padStart(2, "0"));
}
console.log(monthList); // ["01", "02", "03", ..., "12"]
// 实际应用:格式化价格
function formatPrice(price) {
return `¥${price.toFixed(2).padStart(8, '0')}`;
}
console.log(formatPrice(9.5)); // "¥00009.50"
console.log(formatPrice(123.45)); // "¥00123.45"
2. 字符串清理方法
// ES10新增:trimStart()和trimEnd()
let messyString = " Hello World! ";
console.log("'" + messyString.trimStart() + "'"); // 'Hello World! '
console.log("'" + messyString.trimEnd() + "'"); // ' Hello World!'
console.log("'" + messyString.trim() + "'"); // 'Hello World!'
// 等同的方法名
console.log("'" + messyString.trimLeft() + "'"); // 'Hello World! '
console.log("'" + messyString.trimRight() + "'"); // ' Hello World!'
// 实际应用:清理用户输入
function cleanInput(input) {
return input.trim().replace(/\s+/g, ' ');
}
console.log(cleanInput(" Hello there! ")); // "Hello there!"
🎨 数组操作新特性
1. flat和flatMap
// ES10新增:数组的flat()和flatMap()
// flat()方法:将嵌套数组"拉平"
let nestedArray = [1, [2, 3], [4, [5, 6]]];
console.log(nestedArray.flat()); // [1, 2, 3, [4, [5, 6]]]
console.log(nestedArray.flat(2)); // [1, 2, 3, 4, [5, 6]]
console.log(nestedArray.flat(Infinity)); // [1, 2, 3, 4, 5, 6]
// flatMap()方法:先映射再拉平
let sentences = [
"Hello world",
"How are you",
"Fine thank you"
];
// 使用map + flat的组合
let words1 = sentences.map(sentence => sentence.split(' ')).flat();
console.log(words1); // ["Hello", "world", "How", "are", "you", "Fine", "thank", "you"]
// 使用flatMap一步完成
let words2 = sentences.flatMap(sentence => sentence.split(' '));
console.log(words2); // ["Hello", "world", "How", "are", "you", "Fine", "thank", "you"]
// 实际应用:处理嵌套的数据结构
let categories = [
{
name: "电子产品",
items: ["手机", "电脑", "平板"]
},
{
name: "服装",
items: ["T恤", "裤子", "鞋子"]
}
];
// 使用flatMap获取所有商品
let allItems = categories.flatMap(category =>
category.items.map(item => ({
category: category.name,
item: item
}))
);
console.log(allItems);
// [
// { category: "电子产品", item: "手机" },
// { category: "电子产品", item: "电脑" },
// { category: "电子产品", item: "平板" },
// { category: "服装", item: "T恤" },
// { category: "服装", item: "裤子" },
// { category: "服装", item: "鞋子" }
// ]
🔍 正则表达式增强
1. 具名组匹配
// ES9新增:正则表达式具名组匹配
let str = "今天是2022-10-22";
let reg = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/;
let match = reg.exec(str);
console.log(match);
// 通过具名组访问匹配结果
console.log(match.groups.year); // "2022"
console.log(match.groups.month); // "10"
console.log(match.groups.day); // "22"
// 使用具名组进行替换
let newStr = str.replace(reg, '$<day>/$<month>/$<year>');
console.log(newStr); // "22/10/2022"
// 实际应用:解析URL参数
let url = "https://example.com/search?q=javascript&lang=en&page=1";
let urlRegex = /(?<domain>[^/]+)\/search\?(?<params>.+)/;
let urlMatch = urlRegex.exec(url);
if (urlMatch) {
console.log("域名:", urlMatch.groups.domain); // "example.com"
console.log("参数:", urlMatch.groups.params); // "q=javascript&lang=en&page=1"
}
2. 正则表达式中的反向断言
// ES9新增:正向和反向断言
// 正向先行断言:(?=pattern)
let str1 = "JavaScript123Python456Java";
let positiveLookahead = /\w+(?=\d+)/g;
console.log(str1.match(positiveLookahead)); // ["JavaScript", "Python", "Java"]
// 正向否定断言:(?!pattern)
let positiveNegation = /\w+(?!\d+)/g;
console.log(str1.match(positiveNegation)); // ["ava", "ython", "ava"]
// 反向先行断言:(?<=pattern)
let str2 = "100$200$300$";
let negativeLookbehind = /(?<=\$)\d+/g;
console.log(str2.match(negativeLookbehind)); // ["100", "200", "300"]
// 反向否定断言:(?<!pattern)
let str3 = "abc100def200ghi";
let negativeNegation = /(?<!\d)\d+/g;
console.log(str3.match(negativeNegation)); // ["100", "200"]
3. dotAll模式
// ES2018新增:dotAll模式,使.可以匹配换行符
// 传统模式下,.不匹配换行符
let text1 = `Hello
World`;
console.log(/Hello.World/.test(text1)); // false
// 使用dotAll模式
console.log(/Hello.World/s.test(text1)); // true
// 实际应用:匹配多行代码块
let code = `
function test() {
console.log('Hello');
return true;
}
`;
// 使用dotAll模式匹配整个函数
let functionRegex = /function[\s\S]*?}/; // 传统方式
let functionRegexDotAll = /function.*?/s; // dotAll方式
console.log(functionRegex.exec(code));
console.log(functionRegexDotAll.exec(code));
🛠️ 对象转换新方法
1. Object.fromEntries
// ES10新增:Object.fromEntries(),允许将数组转化为对象
// 用处1:Map转对象
let m = new Map();
m.set("name", "tiecui");
m.set("age", 13);
console.log(Object.fromEntries(m));
// { name: "tiecui", age: 13 }
// 用处2:URLSearchParams转对象
let str = "name=xiaoming&age=18&city=beijing";
let searchParams = new URLSearchParams(str);
console.log(Object.fromEntries(searchParams));
// { name: "xiaoming", age: "18", city: "beijing" }
// 用处3:对象转换处理
let obj4 = {
"a": ["a1", "a2", "a3"],
"b": ["b1", "b2"],
"c": ["c1"]
};
// 统计每个键对应的数组长度
let entries = Object.entries(obj4).map(([key, value]) => [key, value.length]);
let result = Object.fromEntries(entries);
console.log(result); // { a: 3, b: 2, c: 1 }
// 实际应用:查询字符串处理
function parseQueryString(queryString) {
if (queryString.startsWith('?')) {
queryString = queryString.slice(1);
}
const params = new URLSearchParams(queryString);
const result = Object.fromEntries(params);
// 尝试转换数字和布尔值
Object.keys(result).forEach(key => {
const value = result[key];
if (value === 'true') result[key] = true;
else if (value === 'false') result[key] = false;
else if (!isNaN(value) && value !== '') result[key] = Number(value);
});
return result;
}
console.log(parseQueryString('?name=张三&age=25&active=true'));
// { name: "张三", age: 25, active: true }
📊 Symbol新特性
1. Symbol.description
// ES10新增:Symbol.description属性
let s1 = Symbol("name");
console.log(s1.description); // "name"
let s2 = Symbol();
console.log(s2.description); // undefined
let s3 = Symbol("这是一个很长的描述");
console.log(s3.description); // "这是一个很长的描述"
// 实际应用:调试和日志记录
function createDebugSymbol(type) {
const symbol = Symbol(type);
console.log(`创建Symbol: ${symbol.description}, ${symbol.toString()}`);
return symbol;
}
const USER_SYMBOL = createDebugSymbol('User');
const PRODUCT_SYMBOL = createDebugSymbol('Product');
🌐 模块化新特性
1. import.meta
// ES11新增:import.meta
// 返回一个对象,包含当前模块的信息
// 在模块文件中使用
console.log(import.meta.url); // 返回当前模块的文件URL
// 获取当前脚本的目录
const currentDir = new URL('.', import.meta.url).href;
console.log('当前目录:', currentDir);
// 动态获取资源路径
function getImagePath(imageName) {
return new URL(`./images/${imageName}`, import.meta.url).href;
}
console.log('图片路径:', getImagePath('logo.png'));
// 条件加载模块
async function loadModule(moduleName) {
if (import.meta.env === 'development') {
const module = await import(`./modules/${moduleName}.dev.js`);
return module;
} else {
const module = await import(`./modules/${moduleName}.prod.js`);
return module;
}
}
2. 导出和重新导出
// ES11新增:无损地重新导出模块
// 原来的方式(会丢失模块信息)
// export { default as User } from './User';
// 新的方式(保留模块信息)
export * as obj from "./module";
// 实际应用:创建统一的导出入口
// 在一个index.js文件中重新导出所有模块
export * as utils from './utils.js';
export * as components from './components.js';
export * as services from './services.js';
// 也可以选择性重新导出
export { User, Product } from './models.js';
export { default as Database } from './database.js';
// 使用时
import { utils, components, User } from './index.js';
🔄 观察者模式实现
1. 完整的观察者模式
// 观察者模式实现
class Subject {
constructor() {
this.observers = [];
}
// 添加观察者
add(observer) {
this.observers.push(observer);
}
// 通知所有观察者
notify() {
this.observers.forEach((item) => {
console.log("通知观察者:", item);
item.update();
});
}
// 移除观察者
remove(observer) {
this.observers = this.observers.filter(item => item !== observer);
}
}
class Observer {
constructor(name) {
this.name = name;
}
update() {
console.log("观察者被通知:", this.name);
}
}
// 使用观察者模式
const subject = new Subject();
const observer1 = new Observer("tzd");
const observer2 = new Observer("小明");
subject.add(observer1);
subject.add(observer2);
// 通知所有观察者
subject.notify();
// 移除一个观察者
subject.remove(observer1);
console.log("移除tzd后再次通知:");
subject.notify();
🎯 综合应用示例
1. 异步任务队列管理器
// 基于Promise和生成器的任务队列管理器
class AsyncTaskQueue {
constructor() {
this.tasks = [];
this.isRunning = false;
}
// 添加任务
addTask(task) {
this.tasks.push({
id: Date.now() + Math.random(),
task,
status: 'pending'
});
}
// 执行所有任务
async runAll() {
if (this.isRunning) {
throw new Error('任务队列正在运行中');
}
this.isRunning = true;
const results = [];
for (const taskItem of this.tasks) {
try {
taskItem.status = 'running';
const result = await taskItem.task();
taskItem.status = 'completed';
results.push({ success: true, result, task: taskItem });
} catch (error) {
taskItem.status = 'failed';
results.push({ success: false, error, task: taskItem });
}
}
this.isRunning = false;
return results;
}
// 使用生成器逐个执行任务
async *runWithGenerator() {
for (const taskItem of this.tasks) {
try {
taskItem.status = 'running';
const result = yield taskItem.task();
taskItem.status = 'completed';
yield { success: true, result, task: taskItem };
} catch (error) {
taskItem.status = 'failed';
yield { success: false, error, task: taskItem };
}
}
}
// 获取任务状态
getTaskStatus() {
return this.tasks.map(task => ({
id: task.id,
status: task.status
}));
}
// 清空队列
clear() {
if (!this.isRunning) {
this.tasks = [];
}
}
}
// 使用任务队列管理器
async function demonstrateTaskQueue() {
const queue = new AsyncTaskQueue();
// 添加任务
queue.addTask(() => {
return new Promise(resolve => {
setTimeout(() => resolve('任务1完成'), 1000);
});
});
queue.addTask(() => {
return new Promise(resolve => {
setTimeout(() => resolve('任务2完成'), 800);
});
});
queue.addTask(() => {
return new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('任务3失败')), 600);
});
});
// 执行所有任务
const results = await queue.runAll();
console.log('执行结果:', results);
// 查看任务状态
console.log('任务状态:', queue.getTaskStatus());
}
demonstrateTaskQueue();
2. 响应式数据管理
// 使用Proxy和观察者模式实现响应式数据管理
class ReactiveData {
constructor(initialData = {}) {
this.data = this.createReactiveObject(initialData);
this.subscribers = new Map();
}
// 创建响应式对象
createReactiveObject(obj) {
return new Proxy(obj, {
get: (target, key) => {
const value = target[key];
// 如果是对象,递归创建响应式
if (value && typeof value === 'object' && !value._isReactive) {
target[key] = this.createReactiveObject(value);
}
return target[key];
},
set: (target, key, value) => {
const oldValue = target[key];
target[key] = value;
// 通知订阅者
this.notifySubscribers(key, value, oldValue);
return true;
}
});
}
// 订阅数据变化
subscribe(key, callback) {
if (!this.subscribers.has(key)) {
this.subscribers.set(key, new Set());
}
this.subscribers.get(key).add(callback);
}
// 取消订阅
unsubscribe(key, callback) {
if (this.subscribers.has(key)) {
this.subscribers.get(key).delete(callback);
}
}
// 通知订阅者
notifySubscribers(key, newValue, oldValue) {
if (this.subscribers.has(key)) {
this.subscribers.get(key).forEach(callback => {
callback(newValue, oldValue, key);
});
}
// 通知通配符订阅者
if (this.subscribers.has('*')) {
this.subscribers.get('*').forEach(callback => {
callback(newValue, oldValue, key);
});
}
}
// 计算属性
computed(fn) {
const dependencies = new Set();
const proxy = new Proxy({}, {
get(target, key) {
dependencies.add(key);
return this.data[key];
}.bind(this)
});
const result = fn(proxy);
// 自动订阅依赖的数据
dependencies.forEach(key => {
this.subscribe(key, () => {
// 依赖变化时重新计算
const newResult = fn(proxy);
console.log(`计算属性更新: ${newResult}`);
});
});
return result;
}
}
// 使用响应式数据管理
const reactive = new ReactiveData({
user: {
name: '张三',
age: 25
},
settings: {
theme: 'light',
language: 'zh-CN'
}
});
// 订阅数据变化
reactive.subscribe('user.name', (newValue, oldValue) => {
console.log(`用户名从 ${oldValue} 变为 ${newValue}`);
});
reactive.subscribe('settings.theme', (newValue, oldValue) => {
console.log(`主题从 ${oldValue} 变为 ${newValue}`);
// 可以在这里更新UI
});
// 计算属性:显示用户信息
const userInfo = reactive.computed((data) => {
return `${data.user.name} (${data.user.age}岁) - ${data.settings.theme}主题`;
});
console.log('初始用户信息:', userInfo);
// 修改数据,会自动触发通知
reactive.data.user.name = '李四';
reactive.data.settings.theme = 'dark';
🎯 小结
- 掌握了Promise的各种用法和异步编程模式
- 学会了生成器函数和迭代器的实际应用
- 理解了对象操作的高级技巧和深拷贝方法
- 掌握了字符串和数组的新特性
- 学会了正则表达式的增强功能
- 了解了模块化编程的新特性
- 实践了观察者模式和响应式编程
这些ES6+的新特性让JavaScript代码更加简洁、高效和易维护,是现代Web开发的重要基础。
下一步学习:TypeScript进阶类型