根据ESMA标准发布,到2025年新特性发布主要包含以下这些特性,大体包含Es9-Es15新特性更新:
JavaScript Es9
/*
ES9 (ECMAScript 2018) 新特性
官方公布的所有新特性详细说明和使用场景
ES2018 (ES9) 包含以下主要新特性:
1. 异步迭代器 (Asynchronous Iteration)
- 引入for await...of循环
- 支持异步可迭代对象
- 异步生成器简化异步迭代器的创建
- 适合遍历异步数据源
2. Promise.finally()
- 无论Promise成功或失败都会执行
- 用于清理资源、隐藏加载状态
- 简化Promise链式调用
3. Rest/Spread 属性
- 剩余属性:从对象中提取部分属性
- 扩展属性:合并对象、创建浅拷贝
- 提高对象操作的灵活性
4. 正则表达式改进
- 命名捕获组:更清晰地标识捕获组
- 反向断言:根据前面的内容匹配目标
- dotAll模式:.匹配包括换行符在内的所有字符
- Unicode属性转义:匹配特定Unicode属性的字符
5. 模板字符串修订
- 允许在带标签的模板字符串中使用转义序列
- 提高带标签模板字符串的灵活性
这些特性进一步增强了JavaScript的异步编程能力、对象操作能力和正则表达式功能,提高了代码的可读性和可维护性。
*/
// ===============================
// 1. 异步迭代器 (Asynchronous Iteration)
// ===============================
// 语法:
// for await (const 变量 of 异步可迭代对象) {
// // 循环体
// }
//
// 异步可迭代对象需要实现:
// [Symbol.asyncIterator]() {
// return {
// next: async () => {
// return { value: 值, done: 布尔值 };
// }
// };
// }
// 使用场景:
// - 遍历异步数据源,如数据流、数据库查询结果
// - 处理多个异步操作的序列
// - 简化异步代码的遍历逻辑
// - 结合async/await使用,提高可读性
// 示例1:基本异步迭代器
const asyncIterable = {
[Symbol.asyncIterator]() {
let i = 0;
return {
next: async () => {
if (i < 5) {
// 模拟异步操作
await new Promise(resolve => setTimeout(resolve, 100));
return { value: i++, done: false };
} else {
return { done: true };
}
}
};
}
};
// 使用for await...of遍历
async function iterateAsync() {
console.log('开始异步迭代:');
for await (const value of asyncIterable) {
console.log(`异步迭代值:${value}`);
}
console.log('异步迭代结束');
}
iterateAsync();
// 示例2:异步生成器(更简洁的异步迭代器实现)
async function* asyncGenerator() {
for (let i = 0; i < 5; i++) {
// 模拟异步操作
await new Promise(resolve => setTimeout(resolve, 100));
yield i;
}
}
async function iterateAsyncGenerator() {
console.log('\n开始异步生成器迭代:');
for await (const value of asyncGenerator()) {
console.log(`异步生成器值:${value}`);
}
console.log('异步生成器迭代结束');
}
iterateAsyncGenerator();
// 示例3:实际应用场景 - 模拟异步API调用序列
async function* fetchItems(urls) {
for (const url of urls) {
console.log(`正在请求:${url}`);
// 模拟API请求
const response = await new Promise(resolve => {
setTimeout(() => resolve(`来自${url}的数据`), 500);
});
yield response;
}
}
async function fetchAllItems() {
const urls = ['/api/item1', '/api/item2', '/api/item3'];
const results = [];
for await (const data of fetchItems(urls)) {
results.push(data);
console.log(`收到数据:${data}`);
}
console.log('\n所有请求完成,结果:', results);
}
fetchAllItems();
// ===============================
// 2. Promise.finally() 方法
// ===============================
// 语法:
// promise.finally(() => {
// // 无论成功或失败都会执行的代码
// });
// 使用场景:
// - 清理资源,如关闭文件、清除定时器、取消订阅
// - 隐藏加载状态,如关闭加载指示器
// - 确保无论异步操作结果如何,都执行某些代码
// - 简化Promise链式调用中的重复代码
// 示例1:基本使用
function fetchWithFinally(url) {
console.log(`开始请求:${url}`);
return new Promise((resolve, reject) => {
setTimeout(() => {
if (url) {
resolve(`成功获取${url}的数据`);
} else {
reject('无效的URL');
}
}, 1000);
})
.then(result => {
console.log(`请求成功:${result}`);
return result;
})
.catch(error => {
console.error(`请求失败:${error}`);
throw error;
})
.finally(() => {
console.log('无论成功失败,都会执行finally');
// 这里可以执行清理操作,如关闭加载状态
console.log('关闭加载指示器');
});
}
// 成功情况
fetchWithFinally('https://api.example.com/data')
.then(result => console.log(`最终结果:${result}`))
.catch(error => console.error(`最终错误:${error}`));
// 失败情况
fetchWithFinally('')
.then(result => console.log(`最终结果:${result}`))
.catch(error => console.error(`最终错误:${error}`));
// 示例2:结合async/await使用
async function fetchWithAsyncAwait(url) {
console.log(`\n开始async/await请求:${url}`);
try {
const result = await new Promise((resolve, reject) => {
setTimeout(() => {
if (url) {
resolve(`成功获取${url}的数据`);
} else {
reject('无效的URL');
}
}, 1000);
});
console.log(`请求成功:${result}`);
return result;
} catch (error) {
console.error(`请求失败:${error}`);
throw error;
} finally {
console.log('无论成功失败,都会执行finally');
console.log('关闭加载指示器');
}
}
fetchWithAsyncAwait('https://api.example.com/users');
// ===============================
// 3. Rest/Spread 属性
// ===============================
// 语法:
// 剩余属性:const { 属性1, ...剩余属性 } = 对象;
// 扩展属性:const 新对象 = { ...对象1, ...对象2 };
// 使用场景:
// - 从对象中提取部分属性,剩余属性组成新对象
// - 合并多个对象
// - 创建对象的浅拷贝
// - 向对象添加新属性,同时保留原有属性
// - 函数参数中的对象解构
// 示例1:剩余属性(Rest Properties)
const user = {
name: '张三',
age: 28,
city: '北京',
email: 'zhangsan@example.com',
phone: '13800138000'
};
// 提取部分属性,剩余属性组成新对象
const { name, age, ...contactInfo } = user;
console.log(`姓名:${name}`); // 输出: 张三
console.log(`年龄:${age}`); // 输出: 28
console.log('联系方式:', contactInfo); // 输出: { city: '北京', email: 'zhangsan@example.com', phone: '13800138000' }
// 示例2:扩展属性(Spread Properties)
const defaultSettings = {
theme: 'light',
fontSize: 14,
language: 'zh-CN'
};
const userSettings = {
fontSize: 16,
notifications: true
};
// 合并对象,userSettings会覆盖defaultSettings中的同名属性
const mergedSettings = { ...defaultSettings, ...userSettings };
console.log('合并后的设置:', mergedSettings);
// 输出: { theme: 'light', fontSize: 16, language: 'zh-CN', notifications: true }
// 示例3:创建对象浅拷贝
const original = { a: 1, b: { c: 2 } };
const copy = { ...original };
console.log(copy); // 输出: { a: 1, b: { c: 2 } }
console.log(copy === original); // 输出: false (不同对象)
console.log(copy.b === original.b); // 输出: true (浅拷贝,嵌套对象还是同一个引用)
// 示例4:在函数参数中使用
function createUser({ name, age, ...otherInfo }) {
console.log(`创建用户:${name},年龄:${age}`);
console.log('其他信息:', otherInfo);
return { id: Math.random().toString(36).substr(2, 9), name, age, ...otherInfo };
}
const newUser = createUser({
name: '李四',
age: 30,
city: '上海',
email: 'lisi@example.com'
});
console.log('新创建的用户:', newUser);
// ===============================
// 4. 正则表达式改进
// ===============================
// ===============================
// 4.1 命名捕获组 (Named Capture Groups)
// ===============================
// 语法:
// /(?<名称>模式)/
// 使用场景:
// - 更清晰地标识捕获组,提高代码可读性
// - 便于后续引用捕获组,无需记住索引
// - 提高正则表达式的可维护性
// - 适合复杂正则表达式,如日期、URL解析
// 示例1:基本使用
const dateRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const dateStr = '2023-12-25';
const match = dateStr.match(dateRegex);
console.log(match); // 完整匹配结果
console.log(`年:${match.groups.year}`); // 输出: 2023
console.log(`月:${match.groups.month}`); // 输出: 12
console.log(`日:${match.groups.day}`); // 输出: 25
// 示例2:在replace中使用命名捕获组
const formattedDate = dateStr.replace(dateRegex, '$<year>年$<month>月$<day>日');
console.log(formattedDate); // 输出: 2023年12月25日
// 示例3:解析URL
const urlRegex = /(?<protocol>https?):\/\/(?<host>[^/:]+)(?<port>:\d+)?(?<path>\/[^?]*)?(?<query>\?.*)?/;
const url = 'https://www.example.com:8080/path/to/resource?param1=value1¶m2=value2';
const urlMatch = url.match(urlRegex);
console.log('URL解析结果:');
console.log(`协议:${urlMatch.groups.protocol}`); // 输出: https
console.log(`主机:${urlMatch.groups.host}`); // 输出: www.example.com
console.log(`端口:${urlMatch.groups.port}`); // 输出: :8080
console.log(`路径:${urlMatch.groups.path}`); // 输出: /path/to/resource
console.log(`查询:${urlMatch.groups.query}`); // 输出: ?param1=value1¶m2=value2
// ===============================
// 4.2 反向断言 (Lookbehind Assertions)
// ===============================
// 语法:
// 正向后向断言:(?<=模式)目标
// 负向后向断言:(?<!模式)目标
// 使用场景:
// - 根据前面的内容匹配目标
// - 提取特定前缀后的内容
// - 避免匹配某些前缀的内容
// - 更精确的模式匹配
// 示例1:正向后向断言
// 匹配$后面的数字,但不包括$
const priceRegex = /(?<=\$)\d+(\.\d{2})?/;
const priceStr = '商品价格:$19.99,原价$29.99';
const prices = priceStr.match(priceRegex);
console.log(prices); // 输出: ['19.99', '.99', index: 6, input: '商品价格:$19.99,原价$29.99', groups: undefined]
// 全局匹配所有价格
const allPrices = priceStr.match(/(?<=\$)\d+(\.\d{2})?/g);
console.log(allPrices); // 输出: ['19.99', '29.99']
// 示例2:负向后向断言
// 匹配不是$开头的数字
const nonDollarPriceRegex = /(?<!\$)\d+(\.\d{2})?/;
const mixedStr = '价格$19.99,数量100,重量2.5kg';
const nonDollarMatches = mixedStr.match(nonDollarPriceRegex);
console.log(nonDollarMatches); // 输出: ['100', undefined, index: 11, input: '价格$19.99,数量100,重量2.5kg', groups: undefined]
// 全局匹配
const allNonDollarMatches = mixedStr.match(/(?<!\$)\d+(\.\d{2})?/g);
console.log(allNonDollarMatches); // 输出: ['100', '2.5']
// ===============================
// 4.3 dotAll 模式 (s 标志)
// ===============================
// 语法:
// /模式/s
// 使用场景:
// - 让.匹配包括换行符在内的所有字符
// - 适合匹配多行文本
// - 简化跨换行符的模式匹配
// 示例:dotAll模式
const multiLineStr = `第一行
第二行
第三行`;
// 传统模式下,.不匹配换行符
const traditionalRegex = /第一行.第二行/;
console.log(traditionalRegex.test(multiLineStr)); // 输出: false
// 使用dotAll模式,.匹配换行符
const dotAllRegex = /第一行.第二行/s;
console.log(dotAllRegex.test(multiLineStr)); // 输出: true
// 示例2:提取HTML标签内容(包括换行)
const htmlStr = `<div class="content">
<h1>标题</h1>
<p>内容</p>
</div>`;
const htmlRegex = /<div class="content">(.*?)<\/div>/s;
const htmlMatch = htmlStr.match(htmlRegex);
console.log(htmlMatch[1]); // 输出: 包含换行的内容
// ===============================
// 4.4 Unicode 属性转义 (Unicode Property Escapes)
// ===============================
// 语法:
// \p{属性名}
// \P{属性名} (否定形式)
// 使用场景:
// - 匹配特定Unicode属性的字符,如字母、数字、符号
// - 支持多种语言的字符匹配
// - 更精确的字符分类
// - 替代传统的字符类,如\w、\d等
// 示例1:匹配所有Unicode字母
const mixedChars = 'Hello 世界 123 🌍';
const letterRegex = /\p{L}+/gu;
console.log(mixedChars.match(letterRegex)); // 输出: ['Hello', '世界']
// 示例2:匹配所有Unicode数字
const digitRegex = /\p{N}+/gu;
console.log(mixedChars.match(digitRegex)); // 输出: ['123']
// 示例3:匹配所有Unicode表情符号
const emojiRegex = /\p{Emoji}/gu;
console.log(mixedChars.match(emojiRegex)); // 输出: ['🌍']
// 示例4:匹配中文汉字
const chineseRegex = /\p{Script=Han}+/gu;
const chineseStr = 'Hello 你好 世界 123';
console.log(chineseStr.match(chineseRegex)); // 输出: ['你好', '世界']
// ===============================
// 5. 模板字符串修订
// ===============================
// 语法:
// 带标签的模板字符串中,允许使用转义序列
// function tag(strings, ...values) {
// // 处理模板字符串
// }
// tag`模板字符串${变量}`
// 使用场景:
// - 允许在带标签的模板字符串中使用转义序列
// - 提高带标签模板字符串的灵活性
// - 支持更多字符在模板字符串中的使用
// 示例:带标签的模板字符串中的转义序列
function tag(strings, ...values) {
console.log('模板字符串数组:', strings);
console.log('插值变量:', values);
return strings.reduce((result, str, i) => {
return result + str + (values[i] || '');
}, '');
}
const name = '张三';
const result = tag`你好,${name}!\n这是换行符\t这是制表符`;
console.log('最终结果:', result);
JavaScript Es10
/*
ES10 (ECMAScript 2019) 新特性
官方公布的所有新特性详细说明和使用场景
ES2019 (ES10) 包含以下主要新特性:
1. Array.prototype.flat()
- 扁平化嵌套数组
- 支持指定深度或Infinity
- 自动移除空项
2. Array.prototype.flatMap()
- 先映射后扁平化
- 相当于map().flat(1)的简写
- 更高效的映射+扁平化操作
3. String.prototype.trimStart() 和 String.prototype.trimEnd()
- 只修剪字符串开头或结尾的空格
- 替代trimLeft()和trimRight()
- 适合格式化文本
4. Object.fromEntries()
- 将键值对数组转换为对象
- Object.entries()的反向操作
- 支持Map转换为对象
5. Symbol.prototype.description
- 获取Symbol的描述字符串
- 便于调试和日志记录
6. 可选的catch绑定
- 无需声明catch块中的error参数
- 简化代码,提高可读性
7. Function.prototype.toString() 修订
- 返回函数的完整源代码,包括注释和空格
- 便于调试和代码分析
8. Array.prototype.sort() 稳定性改进
- 确保排序是稳定的,相等元素保持相对顺序
- 提高排序结果的可预测性
9. JSON 超集支持
- 允许JSON中包含U+2028和U+2029字符
- 与JSON规范完全兼容
10. JSON.stringify() 加强
- 正确处理特殊Unicode字符
- 避免抛出错误
这些特性进一步提高了JavaScript的开发效率和代码可读性,尤其是数组操作和对象转换方面的改进,使得处理复杂数据结构更加便捷。
*/
// ===============================
// 1. Array.prototype.flat() 方法
// ===============================
// 语法:
// array.flat(深度)
// 深度默认为1,使用Infinity可以扁平化任意深度
// 使用场景:
// - 扁平化嵌套数组
// - 处理多层嵌套的数据结构
// - 简化数组操作,避免多层循环
// - 适合处理树形结构数据
// 示例1:基本使用
const nestedArr = [1, 2, [3, 4], [5, [6, 7]]];
// 默认深度为1
const flat1 = nestedArr.flat();
console.log(flat1); // 输出: [1, 2, 3, 4, 5, [6, 7]]
// 指定深度为2
const flat2 = nestedArr.flat(2);
console.log(flat2); // 输出: [1, 2, 3, 4, 5, 6, 7]
// 使用Infinity扁平化任意深度
const deeplyNested = [1, [2, [3, [4, [5]]]]];
const flatInfinity = deeplyNested.flat(Infinity);
console.log(flatInfinity); // 输出: [1, 2, 3, 4, 5]
// 示例2:移除空项
const arrWithEmpty = [1, 2, , 4, , 6];
console.log(arrWithEmpty.flat()); // 输出: [1, 2, 4, 6] (自动移除空项)
// 示例3:实际应用场景 - 扁平化菜单数据
const menuData = [
{ name: '首页', path: '/' },
{
name: '产品',
path: '/products',
children: [
{ name: '产品1', path: '/products/1' },
{ name: '产品2', path: '/products/2' }
]
},
{
name: '关于我们',
path: '/about',
children: [
{
name: '公司介绍',
path: '/about/company',
children: [
{ name: '历史', path: '/about/company/history' },
{ name: '团队', path: '/about/company/team' }
]
}
]
}
];
// 扁平化菜单,获取所有路径
function getAllPaths(menu) {
const paths = [];
function traverse(item) {
paths.push(item.path);
if (item.children) {
for (const child of item.children) {
traverse(child);
}
}
}
for (const item of menu) {
traverse(item);
}
return paths;
}
// 使用flat和flatMap的替代实现
function getAllPathsWithFlat(menu) {
return menu.flatMap(item => {
if (item.children) {
return [item.path, ...getAllPathsWithFlat(item.children)];
}
return [item.path];
});
}
console.log('所有菜单路径:', getAllPaths(menuData));
console.log('使用flatMap获取的菜单路径:', getAllPathsWithFlat(menuData));
// ===============================
// 2. Array.prototype.flatMap() 方法
// ===============================
// 语法:
// array.flatMap(映射函数)
// 相当于 array.map().flat(1)
// 使用场景:
// - 先映射后扁平化数组
// - 替代map+flat的组合,更高效
// - 处理需要同时映射和扁平化的场景
// - 适合生成新数组并扁平化结果
// 示例1:基本使用
const arr = [1, 2, 3, 4];
// 使用map+flat
const mapFlat = arr.map(x => [x, x * 2]).flat();
console.log(mapFlat); // 输出: [1, 2, 2, 4, 3, 6, 4, 8]
// 使用flatMap,更简洁高效
const flatMapped = arr.flatMap(x => [x, x * 2]);
console.log(flatMapped); // 输出: [1, 2, 2, 4, 3, 6, 4, 8]
// 示例2:处理字符串
const sentences = [
'Hello world',
'I love JavaScript',
'ES10 is great'
];
// 分割句子为单词
const words = sentences.flatMap(sentence => sentence.split(' '));
console.log(words); // 输出: ['Hello', 'world', 'I', 'love', 'JavaScript', 'ES10', 'is', 'great']
// 示例3:过滤和映射结合
const numbers = [1, 2, 3, 4, 5];
// 只保留偶数并转换为[num, num*2]格式
const result = numbers.flatMap(num => {
if (num % 2 === 0) {
return [[num, num * 2]];
}
return []; // 不返回任何内容,相当于过滤
});
console.log(result); // 输出: [[2, 4], [4, 8]]
// ===============================
// 3. String.prototype.trimStart() 和 String.prototype.trimEnd() 方法
// ===============================
// 语法:
// string.trimStart()
// string.trimEnd()
// 使用场景:
// - 只修剪字符串开头的空格
// - 只修剪字符串结尾的空格
// - 替代trimLeft()和trimRight()(它们是别名)
// - 适合格式化文本,如对齐显示
// 示例1:基本使用
const str = ' Hello, World! ';
console.log(str.trim()); // 输出: 'Hello, World!' (修剪两端空格)
console.log(str.trimStart()); // 输出: 'Hello, World! ' (只修剪开头空格)
console.log(str.trimEnd()); // 输出: ' Hello, World!' (只修剪结尾空格)
// 示例2:与trimLeft()和trimRight()的关系
console.log(str.trimLeft()); // 输出: 'Hello, World! ' (trimLeft是trimStart的别名)
console.log(str.trimRight()); // 输出: ' Hello, World!' (trimRight是trimEnd的别名)
// 示例3:实际应用场景 - 格式化用户输入
function formatUserInput(input) {
// 移除开头的空格,但保留结尾的换行符等格式
return input.trimStart();
}
const userInput = ' 这是用户输入的内容\n\n 带有多行和空格 ';
console.log('格式化前:', JSON.stringify(userInput));
console.log('格式化后:', JSON.stringify(formatUserInput(userInput)));
// ===============================
// 4. Object.fromEntries() 方法
// ===============================
// 语法:
// Object.fromEntries(可迭代对象)
// 可迭代对象的每个元素必须是[key, value]格式
// 使用场景:
// - 将键值对数组转换为对象
// - 替代Object.entries()的反向操作
// - 用于Map转换为对象
// - 适合处理API返回的键值对数据
// 示例1:基本使用
const entries = [['name', '张三'], ['age', 28], ['city', '北京']];
const obj = Object.fromEntries(entries);
console.log(obj); // 输出: { name: '张三', age: 28, city: '北京' }
// 示例2:Map转换为对象
const map = new Map([
['a', 1],
['b', 2],
['c', 3]
]);
const mapToObj = Object.fromEntries(map);
console.log(mapToObj); // 输出: { a: 1, b: 2, c: 3 }
// 示例3:过滤和转换对象属性
const originalObj = {
name: '张三',
age: 28,
email: 'zhangsan@example.com',
phone: '13800138000'
};
// 只保留指定属性
const filteredObj = Object.fromEntries(
Object.entries(originalObj).filter(([key, value]) =>
['name', 'email'].includes(key)
)
);
console.log(filteredObj); // 输出: { name: '张三', email: 'zhangsan@example.com' }
// 示例4:转换查询字符串为对象
function parseQueryString(query) {
if (!query) return {};
// 移除开头的?
const cleanQuery = query.startsWith('?') ? query.slice(1) : query;
return Object.fromEntries(
cleanQuery.split('&').map(param => {
const [key, value] = param.split('=');
return [decodeURIComponent(key), decodeURIComponent(value)];
})
);
}
const queryString = '?name=张三&age=28&city=北京';
console.log(parseQueryString(queryString)); // 输出: { name: '张三', age: '28', city: '北京' }
// ===============================
// 5. Symbol.prototype.description 属性
// ===============================
// 语法:
// symbol.description
// 使用场景:
// - 获取Symbol的描述字符串
// - 替代String(symbol)或symbol.toString()
// - 便于调试和日志记录
// - 适合处理Symbol作为对象属性的场景
// 示例1:基本使用
const sym1 = Symbol('描述文本');
const sym2 = Symbol();
console.log(sym1.description); // 输出: '描述文本'
console.log(sym2.description); // 输出: undefined (没有描述)
// 与传统方式比较
console.log(String(sym1)); // 输出: 'Symbol(描述文本)'
console.log(sym1.toString()); // 输出: 'Symbol(描述文本)'
// 示例2:在对象中使用
const objWithSymbol = {
[sym1]: 'symbol值',
[Symbol('另一个描述')]: '另一个symbol值'
};
// 获取所有symbol属性并打印描述
for (const symbol of Object.getOwnPropertySymbols(objWithSymbol)) {
console.log(`Symbol描述: ${symbol.description}, 值: ${objWithSymbol[symbol]}`);
}
// ===============================
// 6. 可选的catch绑定 (Optional catch binding)
// ===============================
// 语法:
// try {
// // 可能抛出错误的代码
// } catch {
// // 处理错误,无需使用error参数
// }
// 使用场景:
// - 不需要使用catch块中的错误对象时
// - 简化代码,提高可读性
// - 适合只需要知道发生错误,但不需要错误详情的场景
// - 如错误恢复、默认值处理等
// 示例1:基本使用
function parseJSON(jsonStr) {
try {
return JSON.parse(jsonStr);
} catch {
// 不需要错误对象,只需要返回默认值
return null;
}
}
console.log(parseJSON('{"name": "张三"}')); // 输出: { name: '张三' }
console.log(parseJSON('无效的JSON')); // 输出: null
// 示例2:与传统catch比较
// 传统方式(必须声明error参数)
try {
// 代码
} catch (error) {
// 即使不使用error,也要声明
console.log('发生错误');
}
// 可选catch绑定(无需声明error参数)
try {
// 代码
} catch {
console.log('发生错误');
}
// 示例3:实际应用场景 - 检查环境支持
function checkFeatureSupport() {
try {
// 尝试使用某个新特性
const test = new SomeNewFeature();
return true;
} catch {
// 不关心具体错误,只需要知道不支持
return false;
}
}
// ===============================
// 7. Function.prototype.toString() 修订
// ===============================
// 语法:
// function.toString()
// 使用场景:
// - 获取函数的完整源代码,包括注释和空格
// - 便于调试和代码分析
// - 适合序列化函数或生成文档
// 示例1:基本函数
function testFunction(a, b) {
// 这是一个测试函数
return a + b;
}
console.log(testFunction.toString());
// 输出完整函数源码,包括注释和空格
// 示例2:箭头函数
const arrowFunc = (x) => {
return x * x;
};
console.log(arrowFunc.toString());
// 输出: '(x) => {
// return x * x;
// }'
// 示例3:内置函数
console.log(Array.prototype.map.toString());
// 输出内置map函数的源代码(不同环境可能有所不同)
// ===============================
// 8. Array.prototype.sort() 稳定性改进
// ===============================
// 说明:
// ES2019规范要求sort()方法是稳定的
// 稳定排序意味着相等元素的相对顺序在排序后保持不变
// 使用场景:
// - 对复杂对象进行多次排序
// - 确保排序结果的可预测性
// - 适合需要保持原始顺序的场景
// 示例:稳定排序演示
const items = [
{ name: 'B', value: 2 },
{ name: 'A', value: 1 },
{ name: 'C', value: 2 },
{ name: 'D', value: 1 }
];
// 按value排序
items.sort((a, b) => a.value - b.value);
console.log(items);
// 输出:
// [
// { name: 'A', value: 1 },
// { name: 'D', value: 1 },
// { name: 'B', value: 2 },
// { name: 'C', value: 2 }
// ]
// 注意:value相同的元素,原始顺序保持不变(A在D前,B在C前)
// ===============================
// 9. JSON 超集支持
// ===============================
// 说明:
// ES2019允许JSON中包含U+2028(行分隔符)和U+2029(段落分隔符)字符
// 之前这些字符在JSON字符串中会导致语法错误
// 使用场景:
// - 处理包含特殊Unicode字符的JSON数据
// - 确保与JSON规范完全兼容
// - 适合处理来自各种来源的JSON数据
// 示例:支持U+2028和U+2029字符
const jsonWithSpecialChars = '"包含行分隔符\u2028和段落分隔符\u2029的字符串"';
const parsed = JSON.parse(jsonWithSpecialChars);
console.log(parsed); // 输出: '包含行分隔符 和段落分隔符 的字符串'
// ===============================
// 10. JSON.stringify() 加强
// ===============================
// 说明:
// JSON.stringify()现在能够正确处理一些特殊Unicode字符
// 对于超出范围的Unicode字符,会返回转义序列,而不是抛出错误
// 使用场景:
// - 处理包含特殊Unicode字符的数据
// - 确保JSON.stringify()不会抛出错误
// - 适合序列化各种类型的数据
// 示例:处理特殊Unicode字符
const strWithSpecial = '包含\uD800\uDC00( surrogate pair)的字符串';
console.log(JSON.stringify(strWithSpecial));
// 输出: '"包含\ud800\udc00( surrogate pair)的字符串"'
JavaScript Es11
/*
ES11 (ECMAScript 2020) 新特性
官方公布的所有新特性详细说明和使用场景
ES2020 (ES11) 包含以下主要新特性:
1. 可选链操作符 (?.)
- 安全访问嵌套对象属性,避免TypeError
- 简化多层嵌套属性的访问代码
- 支持访问数组元素和调用函数
2. 空值合并运算符 (??)
- 为null或undefined的值设置默认值
- 替代||运算符,避免将0、''、false等假值替换为默认值
- 与可选链结合使用,效果更佳
3. 动态导入 (import())
- 按需加载模块,提高应用启动性能
- 条件加载不同模块
- 适合大型应用的代码分割
4. 顶层await
- 在模块顶层直接使用await
- 简化模块初始化的异步操作
- 适合配置加载、资源初始化等场景
5. BigInt 类型
- 处理超出Number范围的大整数
- 精确表示和操作大整数
- 适合加密、金融、科学计算等场景
6. globalThis 对象
- 统一访问全局对象,跨环境兼容
- 替代window、self、global等环境特定的全局对象
- 提高代码的可移植性
7. String.prototype.matchAll()
- 获取正则表达式的所有匹配结果,包括捕获组
- 替代exec()方法的循环调用
- 支持全局匹配和命名捕获组
8. Promise.allSettled()
- 并行执行多个异步操作,获取所有结果
- 不会因为一个Promise失败而中断
- 适合批量API请求、资源加载等场景
9. 模块命名空间导出的增强语法
- 简化模块的命名空间导出
- 提高代码的可读性和可维护性
这些特性进一步增强了JavaScript的表达能力和开发效率,特别是在异步编程、大整数处理、跨环境兼容等方面有了显著提升。
*/
// ===============================
// 1. 可选链操作符 (Optional Chaining Operator) ?.
// ===============================
// 语法:
// 对象?.属性
// 对象?.[表达式]
// 函数?.()
// 使用场景:
// - 安全访问嵌套对象属性,避免TypeError: Cannot read property of undefined
// - 简化多层嵌套属性的访问代码
// - 避免冗长的&&检查
// - 适合处理不确定结构的数据,如API返回结果
// 示例1:基本使用
const user = {
name: '张三',
address: {
city: '北京',
street: '朝阳区'
// zipCode 属性不存在
}
};
// 传统方式(冗长且容易出错)
const city1 = user && user.address && user.address.city;
// 可选链方式(简洁安全)
const city2 = user?.address?.city;
// 访问不存在的属性
const zipCode = user?.address?.zipCode;
// 访问更深层次的不存在属性
const district = user?.profile?.address?.district;
console.log(`城市1:${city1}`); // 输出: 北京
console.log(`城市2:${city2}`); // 输出: 北京
console.log(`邮编:${zipCode}`); // 输出: undefined
console.log(`区县:${district}`); // 输出: undefined
// 示例2:使用可选链访问数组元素
const users = [
{ name: '张三', age: 28 },
{ name: '李四', age: 30 }
];
console.log(users?.[0]?.name); // 输出: 张三
console.log(users?.[2]?.name); // 输出: undefined (访问不存在的数组元素)
// 示例3:使用可选链调用函数
const obj = {
sayHello() {
return '你好';
}
// sayGoodbye 函数不存在
};
console.log(obj?.sayHello?.()); // 输出: 你好
console.log(obj?.sayGoodbye?.()); // 输出: undefined (调用不存在的函数)
// 示例4:实际应用场景 - 处理API返回数据
function processApiResponse(response) {
// 安全访问嵌套属性,避免API返回结构变化导致的错误
const data = {
userId: response?.data?.user?.id,
userName: response?.data?.user?.name,
posts: response?.data?.posts?.map(post => ({
id: post?.id,
title: post?.title
})) || []
};
return data;
}
// 模拟API返回数据
const apiResponse = {
data: {
user: {
id: 1,
name: '张三'
},
posts: [
{ id: 1, title: '文章1' },
{ id: 2, title: '文章2' }
]
}
};
console.log(processApiResponse(apiResponse));
console.log(processApiResponse({})); // 处理空响应,不会报错
// ===============================
// 2. 空值合并运算符 (Nullish Coalescing Operator) ??
// ===============================
// 语法:
// 表达式1 ?? 表达式2
// 如果表达式1为null或undefined,则返回表达式2,否则返回表达式1
// 使用场景:
// - 为变量设置默认值,只在值为null或undefined时生效
// - 替代||运算符,避免将0、''、false等假值替换为默认值
// - 适合处理可能包含假值的变量
// 示例1:基本使用
const values = {
name: '', // 空字符串
age: 0, // 数字0
city: null,
country: undefined
};
// 传统 || 运算符的问题:会将0、''、false等假值替换为默认值
console.log(`传统name:${values.name || '默认名称'}`); // 输出: 默认名称 (''被视为假值)
console.log(`传统age:${values.age || 18}`); // 输出: 18 (0被视为假值)
// 空值合并运算符:只在值为null或undefined时才使用默认值
console.log(`?? name:${values.name ?? '默认名称'}`); // 输出: '' (空字符串不是null/undefined)
console.log(`?? age:${values.age ?? 18}`); // 输出: 0 (0不是null/undefined)
console.log(`?? city:${values.city ?? '默认城市'}`); // 输出: 默认城市 (null是nullish值)
console.log(`?? country:${values.country ?? '默认国家'}`); // 输出: 默认国家 (undefined是nullish值)
// 示例2:与可选链结合使用
const userProfile = {
name: '张三'
// address 属性不存在
};
// 安全访问嵌套属性并提供默认值
const userCity = userProfile?.address?.city ?? '未知城市';
console.log(userCity); // 输出: 未知城市
// 示例3:实际应用场景 - 配置项默认值
function createConfig(options) {
return {
// 只有当options.timeout为null或undefined时,才使用默认值3000
timeout: options?.timeout ?? 3000,
// 只有当options.enabled为null或undefined时,才使用默认值true
enabled: options?.enabled ?? true,
// 只有当options.retries为null或undefined时,才使用默认值3
retries: options?.retries ?? 3
};
}
// 测试各种配置情况
console.log(createConfig({})); // 所有默认值
console.log(createConfig({ timeout: 5000 })); // 自定义timeout
console.log(createConfig({ enabled: false })); // enabled为false,不会使用默认值true
console.log(createConfig({ retries: 0 })); // retries为0,不会使用默认值3
// ===============================
// 3. 动态导入 (Dynamic Import)
// ===============================
// 语法:
// import(模块路径)
// 返回一个Promise,解析为模块对象
// 使用场景:
// - 按需加载模块,提高应用启动性能
// - 条件加载模块,根据不同条件加载不同模块
// - 懒加载,只在需要时才加载模块
// - 适合大型应用的代码分割
// 示例1:基本使用
/*
// 动态导入一个模块
async function loadModule() {
try {
const module = await import('./math.js');
console.log(module.add(2, 3)); // 输出: 5
console.log(module.default(2, 3)); // 输出: 6 (如果有默认导出)
} catch (error) {
console.error('模块加载失败:', error);
}
}
loadModule();
*/
// 示例2:条件加载
async function loadFeature(featureName) {
try {
let feature;
// 根据条件加载不同模块
switch (featureName) {
case 'chart':
feature = await import('./chart.js');
break;
case 'map':
feature = await import('./map.js');
break;
case 'editor':
feature = await import('./editor.js');
break;
default:
throw new Error(`未知特性: ${featureName}`);
}
feature.init();
} catch (error) {
console.error('特性加载失败:', error);
}
}
// 示例3:实际应用场景 - React组件懒加载
/*
// React中使用动态导入实现组件懒加载
const LazyComponent = React.lazy(() => import('./LazyComponent'));
function App() {
return (
<React.Suspense fallback={<div>加载中...</div>}>
<LazyComponent />
</React.Suspense>
);
}
*/
// ===============================
// 4. 顶层await (Top-level await)
// ===============================
// 语法:
// // 在模块顶层直接使用await
// const module = await import('./module.js');
// 使用场景:
// - 模块初始化时的异步操作
// - 配置加载,如加载环境变量或配置文件
// - 资源初始化,如连接数据库
// - 简化异步模块的使用
// 示例:顶层await的使用
/*
// 模块顶层
const config = await fetch('/api/config').then(res => res.json());
export const API_BASE_URL = config.apiBaseUrl;
export const API_KEY = config.apiKey;
// 其他模块可以直接使用,无需处理Promise
import { API_BASE_URL } from './config.js';
console.log(API_BASE_URL); // 直接使用配置值
*/
// 示例2:实际应用场景 - 数据库连接
/*
// db.js 模块
import { createConnection } from 'mysql2/promise';
// 顶层await初始化数据库连接
const connection = await createConnection({
host: 'localhost',
user: 'root',
password: 'password',
database: 'mydb'
});
export { connection };
// 在其他模块中直接使用连接
import { connection } from './db.js';
async function queryData() {
const [rows] = await connection.execute('SELECT * FROM users');
return rows;
}
*/
// ===============================
// 5. BigInt 类型
// ===============================
// 语法:
// const bigInt1 = 123n;
// const bigInt2 = BigInt(123);
// 使用场景:
// - 处理超出JavaScript Number类型范围的整数
// - 精确表示和操作大整数
// - 适合处理加密、金融、科学计算等场景
// - 与其他语言的大整数类型交互
// 示例1:基本使用
const bigInt1 = 123n;
const bigInt2 = BigInt(456);
const bigInt3 = BigInt('789');
console.log(bigInt1); // 输出: 123n
console.log(bigInt2); // 输出: 456n
console.log(bigInt3); // 输出: 789n
// 示例2:BigInt运算
const sum = bigInt1 + bigInt2;
const difference = bigInt2 - bigInt1;
const product = bigInt1 * bigInt2;
const quotient = bigInt2 / bigInt1;
const remainder = bigInt2 % bigInt1;
console.log(`和:${sum}`); // 输出: 579n
console.log(`差:${difference}`); // 输出: 333n
console.log(`积:${product}`); // 输出: 56088n
console.log(`商:${quotient}`); // 输出: 3n (整数除法,舍去小数部分)
console.log(`余数:${remainder}`); // 输出: 99n
// 示例3:BigInt与Number的比较
const num = 123;
const big = 123n;
console.log(num == big); // 输出: true (宽松相等,类型转换)
console.log(num === big); // 输出: false (严格相等,类型不同)
console.log(num < big); // 输出: false
console.log(num <= big); // 输出: true
// 示例4:处理超出Number范围的大整数
// Number类型可以安全表示的最大整数是2^53 - 1
const maxSafeInteger = Number.MAX_SAFE_INTEGER;
console.log(maxSafeInteger); // 输出: 9007199254740991
// 超出范围的整数,Number会丢失精度
const unsafeNum = maxSafeInteger + 1;
const unsafeNum2 = maxSafeInteger + 2;
console.log(unsafeNum === unsafeNum2); // 输出: true (精度丢失,两个不同的数相等)
// BigInt可以精确表示任意大的整数
const safeBigInt1 = BigInt(maxSafeInteger) + 1n;
const safeBigInt2 = BigInt(maxSafeInteger) + 2n;
console.log(safeBigInt1); // 输出: 9007199254740992n
console.log(safeBigInt2); // 输出: 9007199254740993n
console.log(safeBigInt1 === safeBigInt2); // 输出: false (精确表示,不丢失精度)
// 示例5:实际应用场景 - 处理大ID
function generateBigId() {
// 生成一个大整数ID,适合分布式系统
return BigInt(Date.now()) * 1000n + BigInt(Math.floor(Math.random() * 1000));
}
const bigId = generateBigId();
console.log(`生成的大ID:${bigId}`); // 输出类似: 1672531200000123n
// ===============================
// 6. globalThis 对象
// ===============================
// 语法:
// globalThis
// 使用场景:
// - 统一访问全局对象,无论在什么环境(浏览器、Node.js、Web Workers等)
// - 替代window、self、global等环境特定的全局对象
// - 适合跨环境的JavaScript代码
// - 提高代码的可移植性和兼容性
// 示例1:基本使用
console.log(globalThis); // 输出当前环境的全局对象
// 在浏览器中:globalThis === window
// 在Node.js中:globalThis === global
// 在Web Workers中:globalThis === self
// 示例2:跨环境代码
function getGlobalObject() {
// 传统方式(需要处理不同环境)
/*
let globalObj;
if (typeof window !== 'undefined') {
globalObj = window;
} else if (typeof self !== 'undefined') {
globalObj = self;
} else if (typeof global !== 'undefined') {
globalObj = global;
} else {
throw new Error('无法确定全局对象');
}
return globalObj;
*/
// 使用globalThis(简洁统一)
return globalThis;
}
console.log(getGlobalObject() === globalThis); // 输出: true
// 示例3:在函数中使用globalThis
function setGlobalVar(name, value) {
globalThis[name] = value;
}
setGlobalVar('myGlobalVar', '这是一个全局变量');
console.log(globalThis.myGlobalVar); // 输出: 这是一个全局变量
// ===============================
// 7. String.prototype.matchAll() 方法
// ===============================
// 语法:
// string.matchAll(正则表达式)
// 返回一个迭代器,包含所有匹配结果
// 使用场景:
// - 获取正则表达式的所有匹配结果,包括捕获组
// - 替代exec()方法的循环调用
// - 适合需要获取所有匹配项详细信息的场景
// - 支持全局匹配和捕获组
// 示例1:基本使用
const str = 'Hello 123, World 456, Welcome 789';
const regex = /(\w+) (\d+)/g;
// 传统方式:使用exec()循环
let match;
console.log('使用exec()方法:');
while ((match = regex.exec(str)) !== null) {
console.log(`匹配:${match[0]}, 单词:${match[1]}, 数字:${match[2]}`);
}
// 重置正则表达式的lastIndex
regex.lastIndex = 0;
// 使用matchAll()方法
console.log('\n使用matchAll()方法:');
for (const match of str.matchAll(regex)) {
console.log(`匹配:${match[0]}, 单词:${match[1]}, 数字:${match[2]}`);
}
// 示例2:将迭代器转换为数组
const matches = [...str.matchAll(regex)];
console.log('\n匹配结果数组:');
console.log(matches);
// 示例3:使用named capture groups
const dateStr = '2023-12-25, 2024-01-01';
const dateRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/g;
for (const match of dateStr.matchAll(dateRegex)) {
console.log(`日期:${match[0]}`);
console.log(`年:${match.groups.year}, 月:${match.groups.month}, 日:${match.groups.day}`);
}
// ===============================
// 8. Promise.allSettled() 方法
// ===============================
// 语法:
// Promise.allSettled(可迭代对象)
// 返回一个Promise,解析为所有Promise的结果数组
// 每个结果对象包含status(fulfilled或rejected)和对应的value或reason
// 使用场景:
// - 并行执行多个异步操作,无论成功或失败都要获取结果
// - 与Promise.all()不同,不会因为一个Promise失败而中断
// - 适合需要处理所有异步操作结果的场景
// - 如批量API请求、资源加载等
// 示例1:基本使用
const promise1 = Promise.resolve('成功1');
const promise2 = Promise.reject('失败2');
const promise3 = Promise.resolve('成功3');
Promise.allSettled([promise1, promise2, promise3])
.then(results => {
console.log('Promise.allSettled 结果:');
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Promise ${index + 1} 成功:${result.value}`);
} else {
console.log(`Promise ${index + 1} 失败:${result.reason}`);
}
});
});
// 对比 Promise.all()(一个失败就会中断)
Promise.all([promise1, promise2, promise3])
.then(results => {
console.log('Promise.all 结果:', results);
})
.catch(error => {
console.log('Promise.all 失败:', error);
});
// 示例2:实际应用场景 - 批量加载资源
async function loadAllResources(urls) {
const promises = urls.map(url => {
return fetch(url)
.then(response => response.json())
.catch(error => {
// 捕获错误,避免影响其他请求
return Promise.reject(`加载 ${url} 失败: ${error.message}`);
});
});
const results = await Promise.allSettled(promises);
// 分离成功和失败的结果
const successfulResults = results
.filter(result => result.status === 'fulfilled')
.map(result => result.value);
const failedResults = results
.filter(result => result.status === 'rejected')
.map(result => result.reason);
return {
successful: successfulResults,
failed: failedResults,
total: results.length
};
}
// 模拟资源URLs
const resourceUrls = [
'https://api.example.com/data1',
'https://api.example.com/data2',
'https://api.example.com/invalid-url',
'https://api.example.com/data3'
];
// 调用函数
loadAllResources(resourceUrls).then(result => {
console.log('\n资源加载结果:');
console.log(`成功:${result.successful.length}个`);
console.log(`失败:${result.failed.length}个`);
console.log(`总计:${result.total}个`);
});
// ===============================
// 9. 模块命名空间导出的增强语法
// ===============================
// 语法:
// export * as 命名空间 from '模块路径';
// 使用场景:
// - 简化模块的命名空间导出
// - 替代先import再export的方式
// - 适合组织和导出多个模块
// - 提高代码的可读性和可维护性
// 示例:
/*
// 传统方式
import * as math from './math.js';
export { math };
// 增强语法
// export * as math from './math.js';
// 在其他模块中使用
import { math } from './index.js';
console.log(math.add(2, 3));
*/
JavaScript Es12
/*
ES12 (ECMAScript 2021) 新特性
官方公布的所有新特性详细说明和使用场景
ES2021 (ES12) 包含以下主要新特性:
1. String.prototype.replaceAll()
- 替换字符串中所有匹配项
- 替代传统replace()需要使用全局正则表达式的方式
- 简化文本替换操作
2. Promise.any()
- 只要有一个Promise成功就返回其结果
- 所有都失败才返回失败
- 提高系统容错性,适合构建高可用系统
3. 逻辑赋值运算符
- &&=: 逻辑与赋值,只有左边为真时才赋值
- ||=: 逻辑或赋值,只有左边为假时才赋值
- ??=: 空值合并赋值,只有左边为null或undefined时才赋值
- 简化条件赋值语句,提高代码可读性
4. 数字分隔符
- 使用下划线分隔数字,提高可读性
- 支持各种数字类型(整数、小数、二进制、十六进制等)
- 适合金融、科学计算等领域
5. WeakRef 和 FinalizationRegistry
- WeakRef: 创建对象的弱引用,不会阻止垃圾回收
- FinalizationRegistry: 跟踪对象生命周期,在对象被回收后执行清理
- 适合缓存、资源管理等场景,需要谨慎使用
这些特性进一步增强了JavaScript的表达能力和开发效率,特别是在字符串处理、异步编程、赋值操作和内存管理方面有了显著提升。
*/
// ===============================
// 1. String.prototype.replaceAll() 方法
// ===============================
// 语法:
// string.replaceAll(搜索值, 替换值)
// 使用场景:
// - 替换字符串中所有匹配的子字符串,无需使用全局正则表达式
// - 简化多个相同替换的操作
// - 提高代码可读性,更直观易懂
// - 适合处理文本替换,如格式化、清理数据等
// 示例1:基本使用
const str = 'Hello, World! Hello, JavaScript!';
// 传统方式:需要使用全局正则表达式
const result1 = str.replace(/Hello/g, 'Hi');
// 使用replaceAll():更简洁直观
const result2 = str.replaceAll('Hello', 'Hi');
console.log(`原始字符串:${str}`);
console.log(`使用正则replace结果:${result1}`); // 输出: Hi, World! Hi, JavaScript!
console.log(`使用replaceAll结果:${result2}`); // 输出: Hi, World! Hi, JavaScript!
// 示例2:使用replaceAll()替换特殊字符
const text = '苹果,香蕉,橙子,苹果,葡萄,苹果';
console.log(text.replaceAll('苹果', '梨子')); // 输出: 梨子,香蕉,橙子,梨子,葡萄,梨子
// 示例3:与正则表达式结合使用
const regexStr = 'Hello 123, Hello 456, Hello 789';
// 使用全局正则表达式
console.log(regexStr.replaceAll(/Hello \d+/g, 'Hi')); // 输出: Hi, Hi, Hi
// 示例4:实际应用场景 - 清理用户输入
function sanitizeInput(input) {
return input
.replaceAll('<', '<')
.replaceAll('>', '>')
.replaceAll('&', '&')
.replaceAll('"', '"')
.replaceAll("'", ''');
}
const userInput = '<script>alert("恶意脚本");</script>';
console.log('清理前:', userInput);
console.log('清理后:', sanitizeInput(userInput));
// ===============================
// 2. Promise.any() 方法
// ===============================
// 语法:
// Promise.any(可迭代对象)
// 只要有一个Promise成功就返回其结果,所有都失败才返回失败
// 使用场景:
// - 从多个API获取数据,只要有一个成功就使用
// - 提高系统容错性,一个服务不可用还有其他备选
// - 与Promise.all()(所有成功才成功)、Promise.race()(第一个完成,无论成功失败)互补
// - 适合构建高可用系统
// 示例1:基本使用
const promiseSuccess1 = new Promise((resolve) => {
setTimeout(() => resolve('成功1'), 300);
});
const promiseSuccess2 = new Promise((resolve) => {
setTimeout(() => resolve('成功2'), 200);
});
const promiseSuccess3 = new Promise((resolve) => {
setTimeout(() => resolve('成功3'), 100);
});
// 只要有一个成功就返回
Promise.any([promiseSuccess1, promiseSuccess2, promiseSuccess3])
.then(result => {
console.log('Promise.any 成功结果:', result); // 输出: 成功3(最快成功的)
})
.catch(error => {
console.error('Promise.any 失败:', error);
});
// 示例2:所有Promise都失败的情况
const promiseFail1 = Promise.reject('失败1');
const promiseFail2 = Promise.reject('失败2');
const promiseFail3 = Promise.reject('失败3');
Promise.any([promiseFail1, promiseFail2, promiseFail3])
.then(result => {
console.log('Promise.any 成功结果:', result);
})
.catch(error => {
console.error('Promise.any 失败:', error); // 输出: AggregateError: All promises were rejected
console.error('所有失败原因:', error.errors); // 输出: ['失败1', '失败2', '失败3']
});
// 示例3:与其他Promise方法的比较
const promiseA = new Promise(resolve => setTimeout(() => resolve('A成功'), 100));
const promiseB = new Promise(resolve => setTimeout(() => resolve('B成功'), 50));
const promiseC = new Promise((resolve, reject) => setTimeout(() => reject('C失败'), 75));
console.log('=== Promise.any() ===');
Promise.any([promiseA, promiseB, promiseC]).then(console.log); // 输出: B成功
console.log('=== Promise.race() ===');
Promise.race([promiseA, promiseB, promiseC]).then(console.log, console.error); // 输出: C失败(最快完成的,虽然是失败)
console.log('=== Promise.all() ===');
Promise.all([promiseA, promiseB]).then(console.log, console.error); // 输出: ['A成功', 'B成功']
// 示例4:实际应用场景 - 从多个CDN加载资源
async function loadResourceFromCDN(urls) {
const promises = urls.map(url => {
return fetch(url)
.then(response => {
if (!response.ok) {
throw new Error(`加载 ${url} 失败: ${response.status}`);
}
return response.text();
});
});
try {
const content = await Promise.any(promises);
console.log('成功从CDN加载资源');
return content;
} catch (error) {
console.error('所有CDN都加载失败:', error);
throw new Error('无法加载资源');
}
}
// 模拟CDN URL列表
const cdnUrls = [
'https://cdn1.example.com/resource.js',
'https://cdn2.example.com/resource.js',
'https://cdn3.example.com/resource.js'
];
loadResourceFromCDN(cdnUrls)
.then(content => console.log('资源内容:', content.slice(0, 50) + '...'))
.catch(error => console.error('加载失败:', error));
// ===============================
// 3. 逻辑赋值运算符
// ===============================
// 语法:
// &&=: 逻辑与赋值,只有左边为真时才赋值
// ||=: 逻辑或赋值,只有左边为假时才赋值
// ??=: 空值合并赋值,只有左边为null或undefined时才赋值
// 使用场景:
// - 简化条件赋值语句
// - 提高代码可读性
// - 减少不必要的赋值操作
// - 适合设置默认值、更新状态等场景
// 示例1:&&= 逻辑与赋值
let a = 10;
a &&= 20; // 等同于 a = a && 20; (10 && 20 = 20)
console.log(a); // 输出: 20
let b = 0;
b &&= 20; // 0 && 20 = 0
console.log(b); // 输出: 0
// 示例2:||= 逻辑或赋值
let c = '';
c ||= '默认值'; // 等同于 c = c || '默认值'; ('', 假值,所以赋值)
console.log(c); // 输出: 默认值
let d = '已有值';
d ||= '默认值'; // '已有值' 是真值,所以不赋值
console.log(d); // 输出: 已有值
// 示例3:??= 空值合并赋值
let e = null;
e ??= '默认值'; // 等同于 e = e ?? '默认值'; (null 是nullish值,所以赋值)
console.log(e); // 输出: 默认值
let f = 0;
f ??= '默认值'; // 0 不是nullish值,所以不赋值
console.log(f); // 输出: 0
let g = '';
g ??= '默认值'; // '' 不是nullish值,所以不赋值
console.log(g); // 输出: ''
// 示例4:实际应用场景 - 配置对象合并
function mergeConfig(options) {
// 只有当options中的属性为null或undefined时,才使用默认值
options.timeout ??= 3000;
options.retries ??= 3;
options.enabled ??= true;
// 只有当enabled为true时,才设置interval
options.enabled &&= {
...options.enabled,
interval: options.timeout / options.retries
};
return options;
}
// 测试配置合并
const config1 = { timeout: 5000 };
console.log('合并配置1:', mergeConfig(config1));
const config2 = { enabled: false };
console.log('合并配置2:', mergeConfig(config2));
const config3 = {};
console.log('合并配置3:', mergeConfig(config3));
// ===============================
// 4. 数字分隔符 (Numeric Separators)
// ===============================
// 语法:
// 数字_数字
// 可以在数字之间使用下划线分隔,提高可读性
// 使用场景:
// - 提高大数字的可读性
// - 便于快速识别数字的位数
// - 支持整数、小数、二进制、八进制、十六进制等
// - 适合金融、科学计算等领域
// 示例1:十进制整数
const billion1 = 1000000000; // 传统方式,难以快速识别位数
const billion2 = 1_000_000_000; // 使用分隔符,可读性更强
console.log(billion1 === billion2); // 输出: true
// 示例2:十进制小数
const pi = 3.141_592_653_589_793;
console.log(pi); // 输出: 3.141592653589793
// 示例3:二进制
const binary = 0b1010_1011_1100_1101;
console.log(binary); // 输出: 43981
// 示例4:八进制
const octal = 0o123_456_700;
console.log(octal); // 输出: 34239744
// 示例5:十六进制
const hex = 0x1A2B_3C4D_5E6F;
console.log(hex); // 输出: 43904110237295
// 示例6:科学计数法
const scientific = 1.23e-4_567;
console.log(scientific); // 输出: 1.23e-4567
// 示例7:实际应用场景 - 金融金额
const price1 = 19999.99; // 传统方式
const price2 = 19_999.99; // 使用分隔符,更直观
console.log(`价格1: ${price1}元`);
console.log(`价格2: ${price2}元`);
// 示例8:使用数字分隔符的注意事项
// 不能在数字开头或结尾
// const invalid1 = _123; // 错误
// const invalid2 = 123_; // 错误
// 不能在小数点旁边
// const invalid3 = 12_.34; // 错误
// const invalid4 = 12._34; // 错误
// 不能在科学计数法的e/E旁边
// const invalid5 = 12e_34; // 错误
// ===============================
// 5. WeakRef 和 FinalizationRegistry
// ===============================
// 语法:
// const weakRef = new WeakRef(目标对象);
// const registry = new FinalizationRegistry(回调函数);
// registry.register(目标对象, 关联值, 清理令牌);
// 使用场景:
// - 创建对象的弱引用,不会阻止垃圾回收
// - 跟踪对象的生命周期,在对象被回收后执行清理操作
// - 适合缓存、事件监听器清理、资源管理等场景
// - 需要谨慎使用,避免依赖不确定的垃圾回收时机
// 示例1:WeakRef 基本使用
let obj = { name: '测试对象' };
let weakRef = new WeakRef(obj);
console.log('弱引用获取对象:', weakRef.deref()); // 输出: { name: '测试对象' }
// 解除强引用,让对象可以被垃圾回收
obj = null;
// 手动触发垃圾回收(在Node.js中需要使用--expose-gc并调用global.gc())
// 注意:浏览器环境中无法手动触发,垃圾回收时机不确定
if (typeof global.gc === 'function') {
global.gc();
console.log('垃圾回收后弱引用获取对象:', weakRef.deref()); // 可能输出: undefined
}
// 示例2:FinalizationRegistry 基本使用
const registry = new FinalizationRegistry((heldValue) => {
console.log(`对象被垃圾回收了,关联值: ${heldValue}`);
});
let obj2 = { name: '要被跟踪的对象' };
// 注册对象,关联值为'obj2-finalizer'
registry.register(obj2, 'obj2-finalizer');
// 解除强引用
obj2 = null;
if (typeof global.gc === 'function') {
global.gc(); // 手动触发垃圾回收
}
// 示例3:实际应用场景 - 缓存实现
class WeakCache {
constructor() {
this.cache = new Map();
this.registry = new FinalizationRegistry(key => {
// 当值被垃圾回收后,清理缓存中的键
this.cache.delete(key);
console.log(`缓存项 ${key} 已清理`);
});
}
set(key, value) {
this.cache.set(key, new WeakRef(value));
// 注册清理
this.registry.register(value, key);
}
get(key) {
const weakRef = this.cache.get(key);
return weakRef ? weakRef.deref() : undefined;
}
has(key) {
return this.cache.has(key) && this.cache.get(key).deref() !== undefined;
}
}
// 使用WeakCache
const cache = new WeakCache();
// 添加缓存项
const cachedObj = { data: '缓存数据' };
cache.set('key1', cachedObj);
console.log('缓存中是否有key1:', cache.has('key1')); // 输出: true
console.log('获取缓存:', cache.get('key1')); // 输出: { data: '缓存数据' }
// 解除强引用
cachedObj = null;
// 触发垃圾回收(如果支持)
if (typeof global.gc === 'function') {
global.gc();
setTimeout(() => {
console.log('垃圾回收后缓存中是否有key1:', cache.has('key1')); // 输出: false
}, 0);
}
JavaScript Es13
/*
ES13 (ECMAScript 2022) 新特性
官方公布的所有新特性详细说明和使用场景
ES2022 (ES13) 包含以下主要新特性:
1. 类的私有属性和方法
- 使用#前缀定义私有属性和方法
- 封装类的内部状态和实现细节
- 提高类的安全性和封装性
- 支持静态私有属性和方法
2. 顶层await
- 在模块顶层直接使用await
- 简化模块初始化的异步操作
- 适合配置加载、资源初始化等场景
3. Array.prototype.at()
- 支持正索引和负索引访问数组元素
- 更简洁地访问数组的最后一个元素
- 提高代码可读性
4. Object.hasOwn()
- 替代Object.prototype.hasOwnProperty.call()
- 更安全地检查对象自身属性
- 避免hasOwnProperty被覆盖的问题
5. Error.cause
- 支持错误链追踪
- 保留原始错误信息
- 便于调试和错误分析
6. RegExp 匹配索引
- 使用/d标志获取匹配的精确位置
- 包含完整匹配和捕获组的索引信息
- 适合文本高亮、替换等场景
7. 模板字符串的标签函数改进
- 更好地支持嵌套模板字符串
- 提高标签函数的灵活性
- 适合国际化、本地化等场景
8. 类的静态块
- 类加载时执行的静态初始化逻辑
- 支持复杂的静态属性初始化
- 处理静态属性之间的依赖关系
这些特性进一步增强了JavaScript的面向对象能力、异步编程支持、数组操作、错误处理和正则表达式功能,提高了代码的可读性、安全性和开发效率。
*/
// ===============================
// 1. 类的私有属性和方法 (Private Fields and Methods)
// ===============================
// 语法:
// class 类名 {
// #私有属性;
//
// constructor() {
// this.#私有属性 = 值;
// }
//
// #私有方法() {
// // 方法体
// }
//
// 公共方法() {
// this.#私有方法();
// }
// }
// 使用场景:
// - 封装类的内部状态和实现细节
// - 避免外部访问和修改内部属性
// - 提高类的封装性和安全性
// - 适合构建复杂的类层次结构
// 示例1:基本使用
class Person {
// 私有属性
#name;
#age;
constructor(name, age) {
this.#name = name;
this.#age = age;
}
// 公共方法访问私有属性
getName() {
return this.#name;
}
getAge() {
return this.#age;
}
// 私有方法
#validateAge(age) {
return age > 0 && age < 150;
}
// 公共方法调用私有方法
setAge(newAge) {
if (this.#validateAge(newAge)) {
this.#age = newAge;
return true;
}
return false;
}
}
const person = new Person('张三', 28);
console.log(person.getName()); // 输出: 张三
console.log(person.getAge()); // 输出: 28
console.log(person.setAge(30)); // 输出: true
console.log(person.getAge()); // 输出: 30
console.log(person.setAge(200)); // 输出: false(调用私有方法验证失败)
// 外部无法直接访问私有属性和方法
// console.log(person.#name); // 语法错误
// console.log(person.#validateAge(30)); // 语法错误
// 示例2:静态私有属性和方法
class Counter {
// 静态私有属性
static #count = 0;
// 静态私有方法
static #increment() {
this.#count++;
}
// 静态公共方法
static next() {
this.#increment();
return this.#count;
}
static getCount() {
return this.#count;
}
}
console.log(Counter.next()); // 输出: 1
console.log(Counter.next()); // 输出: 2
console.log(Counter.getCount()); // 输出: 2
// 示例3:私有字段检测(in操作符支持)
class TestClass {
#privateField;
hasPrivateField() {
return #privateField in this;
}
}
const test = new TestClass();
console.log(test.hasPrivateField()); // 输出: true
// ===============================
// 2. 顶层await (Top-level await)
// ===============================
// 语法:
// // 在模块顶层直接使用await
// const module = await import('./module.js');
// 使用场景:
// - 模块初始化时的异步操作
// - 配置加载,如加载环境变量或配置文件
// - 资源初始化,如连接数据库
// - 简化异步模块的使用
// 示例:顶层await的使用
/*
// 模块顶层
const config = await fetch('/api/config').then(res => res.json());
export const API_BASE_URL = config.apiBaseUrl;
export const API_KEY = config.apiKey;
// 其他模块可以直接使用,无需处理Promise
import { API_BASE_URL } from './config.js';
console.log(API_BASE_URL); // 直接使用配置值
*/
// 示例2:实际应用场景 - 数据库连接
/*
// db.js 模块
import { createConnection } from 'mysql2/promise';
// 顶层await初始化数据库连接
const connection = await createConnection({
host: 'localhost',
user: 'root',
password: 'password',
database: 'mydb'
});
export { connection };
// 在其他模块中直接使用连接
import { connection } from './db.js';
async function queryData() {
const [rows] = await connection.execute('SELECT * FROM users');
return rows;
}
*/
// ===============================
// 3. Array.prototype.at() 方法
// ===============================
// 语法:
// array.at(索引)
// 支持正索引和负索引,负索引表示从末尾开始计数
// 使用场景:
// - 访问数组的最后一个或倒数第n个元素
// - 替代array[array.length - 1]的写法
// - 提高代码可读性
// - 适合需要访问数组末尾元素的场景
// 示例1:基本使用
const numbers = [1, 2, 3, 4, 5];
// 传统方式访问最后一个元素
console.log(numbers[numbers.length - 1]); // 输出: 5
// 使用at()方法访问最后一个元素
console.log(numbers.at(-1)); // 输出: 5
// 访问倒数第二个元素
console.log(numbers.at(-2)); // 输出: 4
// 访问第一个元素
console.log(numbers.at(0)); // 输出: 1
// 访问超出范围的索引
console.log(numbers.at(10)); // 输出: undefined
console.log(numbers.at(-10)); // 输出: undefined
// 示例2:与其他数组方法结合使用
const fruits = ['apple', 'banana', 'cherry', 'date', 'elderberry'];
// 获取数组的最后一个元素并转换为大写
const lastFruit = fruits.at(-1).toUpperCase();
console.log(lastFruit); // 输出: ELDERBERRY
// 示例3:实际应用场景 - 处理用户输入历史
class InputHistory {
constructor(maxSize = 10) {
this.history = [];
this.maxSize = maxSize;
}
addInput(input) {
this.history.push(input);
if (this.history.length > this.maxSize) {
this.history.shift();
}
}
// 获取最近的输入
getRecent(n = 1) {
return this.history.at(-n);
}
// 获取最近的n个输入
getRecentN(n = 3) {
return this.history.slice(-n);
}
}
const history = new InputHistory();
history.addInput('第一个输入');
history.addInput('第二个输入');
history.addInput('第三个输入');
history.addInput('第四个输入');
console.log(history.getRecent()); // 输出: 第四个输入
console.log(history.getRecent(2)); // 输出: 第三个输入
console.log(history.getRecentN(2)); // 输出: ['第三个输入', '第四个输入']
// ===============================
// 4. Object.hasOwn() 方法
// ===============================
// 语法:
// Object.hasOwn(对象, 属性名)
// 检查对象自身是否有指定的属性(不包括继承的属性)
// 使用场景:
// - 替代Object.prototype.hasOwnProperty.call()
// - 更安全地检查对象自身属性
// - 避免hasOwnProperty被覆盖的问题
// - 适合需要准确检查对象自身属性的场景
// 示例1:基本使用
const obj = {
name: '张三',
age: 28
};
console.log(Object.hasOwn(obj, 'name')); // 输出: true(对象自身有name属性)
console.log(Object.hasOwn(obj, 'toString')); // 输出: false(toString是继承的属性)
console.log(Object.hasOwn(obj, 'age')); // 输出: true
console.log(Object.hasOwn(obj, 'gender')); // 输出: false
console.log(Object.hasOwn(obj, 'gender'));
// 示例2:与hasOwnProperty的比较
// 传统方式:容易被覆盖
const objWithOwnHasOwnProperty = {
hasOwnProperty: function() {
return false;
}
};
// 传统方式会失败
// console.log(objWithOwnHasOwnProperty.hasOwnProperty('name')); // 输出: false(被覆盖)
// 使用Object.hasOwn不会受影响
console.log(Object.hasOwn(objWithOwnHasOwnProperty, 'name')); // 输出: false
// 示例3:处理原型链
const parent = { parentProp: '父属性' };
const child = Object.create(parent);
child.childProp = '子属性';
console.log(Object.hasOwn(child, 'childProp')); // 输出: true(自身属性)
console.log(Object.hasOwn(child, 'parentProp')); // 输出: false(继承属性)
console.log(child.parentProp); // 输出: 父属性(可以访问继承属性)
// 示例4:实际应用场景 - 安全的属性遍历
function safeGetOwnPropertyNames(obj) {
const ownProps = [];
for (const prop in obj) {
if (Object.hasOwn(obj, prop)) {
ownProps.push(prop);
}
}
return ownProps;
}
const testObj = {
a: 1,
b: 2
};
// 添加一个继承属性
Object.prototype.c = 3;
console.log('所有属性(包括继承):', Object.keys(testObj)); // 输出: ['a', 'b'](Object.keys只返回自身可枚举属性)
console.log('使用for...in遍历:', safeGetOwnPropertyNames(testObj)); // 输出: ['a', 'b'](只返回自身属性)
// 删除原型链上的属性
delete Object.prototype.c;
// ===============================
// 5. Error.cause 属性
// ===============================
// 语法:
// new Error('错误信息', { cause: 原始错误 });
// 使用场景:
// - 错误链追踪,保留原始错误信息
// - 更详细的错误诊断
// - 便于调试和错误分析
// - 适合复杂系统中的错误处理
// 示例1:基本使用
function fetchData(url) {
return fetch(url)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP错误: ${response.status}`, { cause: response });
}
return response.json();
})
.catch(error => {
// 包装错误,保留原始错误
throw new Error(`获取数据失败: ${error.message}`, { cause: error });
});
}
// 调用函数并处理错误
fetchData('https://api.example.com/invalid-url')
.then(data => console.log(data))
.catch(error => {
console.error('最终错误:', error.message);
console.error('原始错误:', error.cause.message);
console.error('错误堆栈:', error.stack);
// 可以继续访问更深层次的错误
if (error.cause.cause) {
console.error('HTTP响应:', error.cause.cause.status);
}
});
// 示例2:自定义错误类支持cause
class DatabaseError extends Error {
constructor(message, cause) {
super(message, { cause });
this.name = 'DatabaseError';
}
}
function queryDatabase(query) {
try {
// 模拟数据库查询错误
throw new Error('数据库连接失败');
} catch (error) {
throw new DatabaseError(`查询失败: ${query}`, error);
}
}
try {
queryDatabase('SELECT * FROM users');
} catch (error) {
console.error(`${error.name}: ${error.message}`); // 输出: DatabaseError: 查询失败: SELECT * FROM users
console.error(`原因: ${error.cause.message}`); // 输出: 原因: 数据库连接失败
}
// ===============================
// 6. RegExp 匹配索引
// ===============================
// 语法:
// const regex = /模式/d;
// const match = regex.exec(字符串);
// match.indices // 包含匹配的起始和结束索引
// 使用场景:
// - 获取正则表达式匹配的精确位置
// - 用于文本高亮、替换和分析
// - 适合编辑器、IDE等需要精确位置信息的场景
// 示例1:基本使用
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/d;
const dateStr = '今天是2023-12-25,明天是2024-01-01';
const match = regex.exec(dateStr);
console.log('完整匹配:', match[0]); // 输出: 2023-12-25
console.log('年:', match.groups.year); // 输出: 2023
console.log('月:', match.groups.month); // 输出: 12
console.log('日:', match.groups.day); // 输出: 25
// indices属性包含匹配的起始和结束索引
console.log('匹配索引:', match.indices);
// 输出:
// {
// 0: [3, 13], // 完整匹配的起始和结束索引
// 1: [3, 7], // 第一个捕获组(年)的索引
// 2: [8, 10], // 第二个捕获组(月)的索引
// 3: [11, 13], // 第三个捕获组(日)的索引
// groups: {
// year: [3, 7],
// month: [8, 10],
// day: [11, 13]
// }
// }
// 示例2:全局匹配的索引
const globalRegex = /\b\w+\b/dg;
const text = 'Hello World';
let matchResult;
console.log('全局匹配索引:');
while ((matchResult = globalRegex.exec(text)) !== null) {
console.log(`匹配: ${matchResult[0]}, 索引: [${matchResult.indices[0][0]}, ${matchResult.indices[0][1]}]`);
}
// 输出:
// 匹配: Hello, 索引: [0, 5]
// 匹配: World, 索引: [6, 11]
// 示例3:实际应用场景 - 文本高亮
function highlightText(text, pattern) {
const regex = new RegExp(pattern, 'dg');
let result = '';
let lastIndex = 0;
let match;
while ((match = regex.exec(text)) !== null) {
const [start, end] = match.indices[0];
// 添加匹配前的文本
result += text.slice(lastIndex, start);
// 添加高亮的匹配文本
result += `<span class="highlight">${text.slice(start, end)}</span>`;
lastIndex = end;
}
// 添加剩余文本
result += text.slice(lastIndex);
return result;
}
const htmlText = highlightText('今天是2023-12-25,明天是2024-01-01', '\d{4}-\d{2}-\d{2}');
console.log('高亮后的HTML:', htmlText);
// ===============================
// 7. 模板字符串的标签函数改进
// ===============================
// 语法:
// function tag(strings, ...values) {
// // 处理模板字符串
// return processedString;
// }
// tag`模板字符串${变量}`
// 使用场景:
// - 更灵活地处理模板字符串
// - 支持嵌套模板字符串
// - 适合国际化、本地化等场景
// 示例1:基本使用
function myTag(strings, ...values) {
console.log('字符串数组:', strings);
console.log('值数组:', values);
let result = '';
for (let i = 0; i < strings.length; i++) {
result += strings[i];
if (i < values.length) {
result += `[${values[i]}]`;
}
}
return result;
}
const name = '张三';
const age = 28;
const result = myTag`你好,我是${name},今年${age}岁。`;
console.log('标签函数结果:', result);
// 示例2:嵌套模板字符串
function outerTag(strings, ...values) {
return `OUTER: ${strings[0]}${values[0]}${strings[1]}`;
}
function innerTag(strings, ...values) {
return `INNER: ${strings[0]}${values[0]}${strings[1]}`;
}
const nestedResult = outerTag`外部模板${innerTag`内部模板${name}`}结束`;
console.log('嵌套标签结果:', nestedResult);
// 示例3:实际应用场景 - 国际化
const i18n = {
en: {
greeting: 'Hello, {name}! You are {age} years old.'
},
zh: {
greeting: '你好,{name}!你今年{age}岁。'
}
};
function translate(locale, strings, ...values) {
const key = strings.join('{value}');
let translation = i18n[locale][key] || strings.join('{value}');
// 替换占位符
values.forEach((value, index) => {
translation = translation.replace(`{${Object.keys(i18n[locale])[index]}}`, value);
});
return translation;
}
// 使用标签函数进行国际化
const greetingEn = translate.bind(null, 'en');
const greetingZh = translate.bind(null, 'zh');
console.log(greetingEn`greeting`(name, age)); // 输出: Hello, 张三! You are 28 years old.
console.log(greetingZh`greeting`(name, age)); // 输出: 你好,张三!你今年28岁。
// ===============================
// 8. 类的静态块 (Static Blocks)
// ===============================
// 语法:
// class 类名 {
// static {
// // 静态块代码
// }
// }
// 使用场景:
// - 类的静态初始化逻辑
// - 复杂的静态属性初始化
// - 静态属性之间的依赖关系
// - 适合需要在类加载时执行的初始化代码
// 示例1:基本使用
class Config {
static config = null;
// 静态块
static {
// 复杂的静态初始化逻辑
const env = process.env.NODE_ENV || 'development';
const baseUrl = env === 'production' ? 'https://api.example.com' : 'http://localhost:3000';
this.config = {
env,
baseUrl,
apiKey: 'secret-key-' + env
};
}
static getConfig() {
return this.config;
}
}
console.log(Config.getConfig()); // 输出: { env: 'development', baseUrl: 'http://localhost:3000', apiKey: 'secret-key-development' }
// 示例2:多个静态块和静态属性的顺序
class MultipleStaticBlocks {
static value1 = 1;
static {
this.value2 = this.value1 * 2;
}
static value3 = this.value2 * 2;
static {
this.value4 = this.value3 * 2;
}
}
console.log('value1:', MultipleStaticBlocks.value1); // 输出: 1
console.log('value2:', MultipleStaticBlocks.value2); // 输出: 2
console.log('value3:', MultipleStaticBlocks.value3); // 输出: 4
console.log('value4:', MultipleStaticBlocks.value4); // 输出: 8
// 示例3:实际应用场景 - 数据库连接池初始化
class Database {
static pool = null;
static {
// 初始化数据库连接池
const { createPool } = require('mysql2/promise');
this.pool = createPool({
host: 'localhost',
user: 'root',
password: 'password',
database: 'mydb',
connectionLimit: 10
});
console.log('数据库连接池已初始化');
}
static async query(sql, params) {
const [rows] = await this.pool.execute(sql, params);
return rows;
}
static async close() {
await this.pool.end();
console.log('数据库连接池已关闭');
}
}
// 类加载时会自动执行静态块初始化连接池
// 可以直接使用Database.query()进行查询
JavaScript Es14
/*
ES14 (ECMAScript 2023) 新特性
官方公布的所有新特性详细说明和使用场景
ES2023 (ES14) 包含以下主要新特性:
1. Array.prototype.findLast() 和 Array.prototype.findLastIndex()
- 从数组末尾开始查找元素
- 找到符合条件的最后一个元素及其索引
- 简化从后往前查找的操作
2. Array.prototype.toSorted()
- 返回新的已排序数组,原数组不变
- 保持数据不可变性
- 适合函数式编程风格
3. Array.prototype.toReversed()
- 返回新的反转数组,原数组不变
- 保持数据不可变性
- 适合函数式编程风格
4. Array.prototype.with()
- 返回新数组,替换指定索引的元素
- 保持数据不可变性
- 适合更新数组中某个元素
5. Object.groupBy() 和 Map.groupBy()
- 将数组按指定条件分组
- Object.groupBy() 返回对象,键为字符串
- Map.groupBy() 返回Map,键可以是任意类型
- 适合数据分组、统计、分类
6. Symbol.metadataKey
- 用于装饰器元数据的存储
- 支持TC39装饰器提案
- 适合框架和库开发
这些特性进一步增强了JavaScript的数组操作能力,提供了更多保持数据不可变性的方法,同时简化了数据分组和装饰器元数据处理。这些特性提高了代码的可读性、可维护性和开发效率,特别是在函数式编程和数据处理场景中。
*/
// ===============================
// 1. Array.prototype.findLast() 和 Array.prototype.findLastIndex() 方法
// ===============================
// 语法:
// array.findLast(callback(element, index, array), thisArg)
// array.findLastIndex(callback(element, index, array), thisArg)
// 使用场景:
// - 从数组末尾开始查找元素
// - 找到符合条件的最后一个元素
// - 获取符合条件的最后一个元素的索引
// - 适合需要从后往前查找的场景,如日志分析、最近记录等
// 示例1:findLast() 基本使用
const numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
// 从数组末尾开始查找第一个大于3的元素
const lastGreaterThan3 = numbers.findLast(num => num > 3);
console.log(`最后一个大于3的元素:${lastGreaterThan3}`); // 输出: 4
// 传统方式:需要反转数组或从后往前遍历
const traditionalLastGreaterThan3 = [...numbers].reverse().find(num => num > 3);
console.log(`传统方式查找结果:${traditionalLastGreaterThan3}`); // 输出: 4
// 示例2:findLastIndex() 基本使用
const lastGreaterThan3Index = numbers.findLastIndex(num => num > 3);
console.log(`最后一个大于3的元素索引:${lastGreaterThan3Index}`); // 输出: 5
// 传统方式:从后往前遍历
let traditionalIndex = -1;
for (let i = numbers.length - 1; i >= 0; i--) {
if (numbers[i] > 3) {
traditionalIndex = i;
break;
}
}
console.log(`传统方式查找索引:${traditionalIndex}`); // 输出: 5
// 示例3:使用对象数组
const users = [
{ id: 1, name: '张三', active: false },
{ id: 2, name: '李四', active: true },
{ id: 3, name: '王五', active: false },
{ id: 4, name: '赵六', active: true }
];
// 查找最后一个活跃用户
const lastActiveUser = users.findLast(user => user.active);
console.log(`最后一个活跃用户:`, lastActiveUser); // 输出: { id: 4, name: '赵六', active: true }
// 查找最后一个活跃用户的索引
const lastActiveIndex = users.findLastIndex(user => user.active);
console.log(`最后一个活跃用户索引:${lastActiveIndex}`); // 输出: 3
// 示例4:实际应用场景 - 日志分析
const logs = [
{ timestamp: 1680000000, level: 'info', message: '系统启动' },
{ timestamp: 1680000100, level: 'error', message: '数据库连接失败' },
{ timestamp: 1680000200, level: 'warn', message: '内存使用率过高' },
{ timestamp: 1680000300, level: 'error', message: 'API请求超时' },
{ timestamp: 1680000400, level: 'info', message: '系统正常运行' }
];
// 查找最后一个错误日志
const lastErrorLog = logs.findLast(log => log.level === 'error');
console.log(`最后一个错误日志:`, lastErrorLog);
// ===============================
// 2. Array.prototype.toSorted() 方法
// ===============================
// 语法:
// array.toSorted(compareFunction)
// 返回一个新的已排序数组,原数组不变
// 使用场景:
// - 对数组进行排序,不修改原数组
// - 函数式编程风格,保持数据不可变性
// - 适合需要保留原始数组的场景
// - 链式调用中使用
// 示例1:基本使用
const originalArray = [3, 1, 4, 1, 5, 9, 2, 6];
// toSorted() 返回新数组,原数组不变
const sortedArray = originalArray.toSorted();
console.log(`原数组:${originalArray}`); // 输出: 3,1,4,1,5,9,2,6
console.log(`排序后数组:${sortedArray}`); // 输出: 1,1,2,3,4,5,6,9
// 与sort()方法比较(sort()会修改原数组)
const mutableArray = [3, 1, 4, 1, 5];
const sortedMutableArray = mutableArray.sort();
console.log(`sort()原数组:${mutableArray}`); // 输出: 1,1,3,4,5(原数组已修改)
console.log(`sort()返回值:${sortedMutableArray}`); // 输出: 1,1,3,4,5
// 示例2:使用比较函数
const numbersToSort = [10, 5, 8, 1, 7];
// 升序排序
const ascSorted = numbersToSort.toSorted((a, b) => a - b);
console.log(`升序排序:${ascSorted}`); // 输出: 1,5,7,8,10
// 降序排序
const descSorted = numbersToSort.toSorted((a, b) => b - a);
console.log(`降序排序:${descSorted}`); // 输出: 10,8,7,5,1
// 原数组仍保持不变
console.log(`原数组:${numbersToSort}`); // 输出: 10,5,8,1,7
// 示例3:对对象数组排序
const products = [
{ name: '手机', price: 5999 },
{ name: '笔记本电脑', price: 9999 },
{ name: '平板电脑', price: 3999 },
{ name: '智能手表', price: 1999 }
];
// 按价格升序排序
const productsByPriceAsc = products.toSorted((a, b) => a.price - b.price);
console.log('按价格升序排序:', productsByPriceAsc);
// 按价格降序排序
const productsByPriceDesc = products.toSorted((a, b) => b.price - a.price);
console.log('按价格降序排序:', productsByPriceDesc);
// 示例4:链式调用
const data = [5, 3, 8, 1, 2];
const result = data
.filter(num => num > 2)
.toSorted()
.map(num => num * 2);
console.log('链式调用结果:', result); // 输出: [6, 10, 16]
// ===============================
// 3. Array.prototype.toReversed() 方法
// ===============================
// 语法:
// array.toReversed()
// 返回一个新的反转数组,原数组不变
// 使用场景:
// - 反转数组,不修改原数组
// - 函数式编程风格,保持数据不可变性
// - 适合需要保留原始数组顺序的场景
// - 链式调用中使用
// 示例1:基本使用
const original = [1, 2, 3, 4, 5];
// toReversed() 返回新数组,原数组不变
const reversed = original.toReversed();
console.log(`原数组:${original}`); // 输出: 1,2,3,4,5
console.log(`反转后数组:${reversed}`); // 输出: 5,4,3,2,1
// 与reverse()方法比较(reverse()会修改原数组)
const mutable = [1, 2, 3];
const reversedMutable = mutable.reverse();
console.log(`reverse()原数组:${mutable}`); // 输出: 3,2,1(原数组已修改)
console.log(`reverse()返回值:${reversedMutable}`); // 输出: 3,2,1
// 示例2:对象数组反转
const items = [
{ id: 1, name: '第一项' },
{ id: 2, name: '第二项' },
{ id: 3, name: '第三项' }
];
const reversedItems = items.toReversed();
console.log('原对象数组:', items);
console.log('反转后对象数组:', reversedItems);
// 示例3:链式调用
const chainData = [1, 2, 3, 4, 5];
const chainResult = chainData
.filter(num => num % 2 === 0)
.toReversed()
.map(num => num * 3);
console.log('链式调用结果:', chainResult); // 输出: [12, 6]
// ===============================
// 4. Array.prototype.with() 方法
// ===============================
// 语法:
// array.with(index, value)
// 返回一个新数组,指定索引处的元素被替换为新值,原数组不变
// 使用场景:
// - 替换数组中指定索引的元素,不修改原数组
// - 函数式编程风格,保持数据不可变性
// - 适合需要更新数组中某个元素的场景
// - 链式调用中使用
// 示例1:基本使用
const originalWith = [1, 2, 3, 4, 5];
// with() 返回新数组,原数组不变
const newArrayWith = originalWith.with(2, 10);
console.log(`原数组:${originalWith}`); // 输出: 1,2,3,4,5
console.log(`替换后数组:${newArrayWith}`); // 输出: 1,2,10,4,5
// 传统方式:需要创建副本并修改
const traditionalNewArray = [...originalWith];
traditionalNewArray[2] = 10;
console.log(`传统方式替换结果:${traditionalNewArray}`); // 输出: 1,2,10,4,5
// 示例2:使用负索引
const arrayWithNegative = [1, 2, 3, 4, 5];
const newArrayWithNegative = arrayWithNegative.with(-1, 100);
console.log(`原数组:${arrayWithNegative}`); // 输出: 1,2,3,4,5
console.log(`替换最后一个元素:${newArrayWithNegative}`); // 输出: 1,2,3,4,100
// 示例3:对象数组更新
const usersWith = [
{ id: 1, name: '张三', age: 28 },
{ id: 2, name: '李四', age: 30 },
{ id: 3, name: '王五', age: 25 }
];
// 更新索引为1的用户年龄
const updatedUsers = usersWith.with(1, { ...usersWith[1], age: 31 });
console.log('原用户数组:', usersWith);
console.log('更新后用户数组:', updatedUsers);
console.log('是否为同一数组:', usersWith === updatedUsers); // 输出: false
// 示例4:链式调用
const chainDataWith = [1, 2, 3, 4, 5];
const chainResultWith = chainDataWith
.toSorted()
.with(0, 10)
.toReversed();
console.log('链式调用结果:', chainResultWith); // 输出: [5, 4, 3, 2, 10]
// ===============================
// 5. Object.groupBy() 和 Map.groupBy() 方法
// ===============================
// 语法:
// Object.groupBy(items, callback(element, index, array))
// Map.groupBy(items, callback(element, index, array))
// 使用场景:
// - 将数组按指定条件分组
// - Object.groupBy() 返回对象,键为分组条件,值为分组后的数组
// - Map.groupBy() 返回Map,键可以是任意类型,值为分组后的数组
// - 适合数据分组、统计、分类等场景
// 示例1:Object.groupBy() 基本使用
const people = [
{ name: '张三', age: 28, gender: 'male' },
{ name: '李四', age: 30, gender: 'female' },
{ name: '王五', age: 25, gender: 'male' },
{ name: '赵六', age: 35, gender: 'female' },
{ name: '孙七', age: 22, gender: 'male' }
];
// 按性别分组
const groupedByGender = Object.groupBy(people, person => person.gender);
console.log('按性别分组结果:', groupedByGender);
// 输出:
// {
// male: [
// { name: '张三', age: 28, gender: 'male' },
// { name: '王五', age: 25, gender: 'male' },
// { name: '孙七', age: 22, gender: 'male' }
// ],
// female: [
// { name: '李四', age: 30, gender: 'female' },
// { name: '赵六', age: 35, gender: 'female' }
// ]
// }
// 示例2:按年龄范围分组
const groupedByAgeRange = Object.groupBy(people, person => {
if (person.age < 25) return 'young';
if (person.age < 30) return 'middle';
return 'old';
});
console.log('按年龄范围分组结果:', groupedByAgeRange);
// 示例3:Map.groupBy() 基本使用
// 按年龄奇偶性分组,键为布尔值
const groupedByAgeParity = Map.groupBy(people, person => person.age % 2 === 0);
console.log('按年龄奇偶性分组结果(Map):');
for (const [key, value] of groupedByAgeParity) {
console.log(`${key}: ${JSON.stringify(value)}`);
}
// 示例4:使用对象作为分组键(只有Map支持)
const transactions = [
{ id: 1, amount: 100, category: { id: 1, name: '食品' } },
{ id: 2, amount: 200, category: { id: 2, name: '交通' } },
{ id: 3, amount: 150, category: { id: 1, name: '食品' } },
{ id: 4, amount: 50, category: { id: 3, name: '娱乐' } },
{ id: 5, amount: 300, category: { id: 2, name: '交通' } }
];
// 使用category对象作为分组键
const groupedByCategory = Map.groupBy(transactions, transaction => transaction.category);
console.log('按分类对象分组结果:');
for (const [category, items] of groupedByCategory) {
console.log(`${category.name}:${items.length}笔交易,总金额${items.reduce((sum, item) => sum + item.amount, 0)}`);
}
// 示例5:实际应用场景 - 统计销售数据
const sales = [
{ product: '手机', region: '华东', amount: 5000 },
{ product: '笔记本电脑', region: '华南', amount: 8000 },
{ product: '手机', region: '华北', amount: 6000 },
{ product: '平板电脑', region: '华东', amount: 3000 },
{ product: '手机', region: '华南', amount: 7000 },
{ product: '笔记本电脑', region: '华北', amount: 9000 }
];
// 按产品分组
const salesByProduct = Object.groupBy(sales, sale => sale.product);
console.log('按产品分组销售数据:');
for (const [product, items] of Object.entries(salesByProduct)) {
const totalAmount = items.reduce((sum, item) => sum + item.amount, 0);
console.log(`${product}:${items.length}笔销售,总金额${totalAmount}`);
}
// 按地区分组
const salesByRegion = Object.groupBy(sales, sale => sale.region);
console.log('按地区分组销售数据:');
for (const [region, items] of Object.entries(salesByRegion)) {
const totalAmount = items.reduce((sum, item) => sum + item.amount, 0);
console.log(`${region}:${items.length}笔销售,总金额${totalAmount}`);
}
// ===============================
// 6. Symbol.metadataKey(装饰器元数据)
// ===============================
// 语法:
// Symbol.metadataKey
// 使用场景:
// - 用于装饰器元数据的存储
// - 支持TC39装饰器提案
// - 适合需要在类、方法、属性上存储元数据的场景
// - 用于框架和库的开发
// 示例1:基本使用
// 注意:装饰器目前需要在支持的环境中使用,如TypeScript或启用实验性装饰器的JavaScript引擎
/*
function log(target, context) {
// 存储元数据
const metadata = context.metadata[Symbol.metadataKey] || [];
metadata.push({ kind: context.kind, name: context.name });
context.metadata[Symbol.metadataKey] = metadata;
return target;
}
@log
class MyClass {
@log
myMethod() {
// 方法体
}
}
// 获取元数据
const metadata = MyClass[Symbol.metadataKey];
console.log('类元数据:', metadata);
*/
// 示例2:自定义装饰器使用元数据
/*
function deprecated(reason) {
return function(target, context) {
// 存储废弃信息作为元数据
const metadata = context.metadata[Symbol.metadataKey] || [];
metadata.push({ deprecated: true, reason });
context.metadata[Symbol.metadataKey] = metadata;
// 在开发环境中添加警告
if (context.kind === 'method') {
return function(...args) {
console.warn(`${context.name}方法已废弃:${reason}`);
return target.apply(this, args);
};
}
return target;
};
}
class MyService {
@deprecated('请使用newMethod替代')
oldMethod() {
return '旧方法';
}
newMethod() {
return '新方法';
}
}
const service = new MyService();
service.oldMethod(); // 输出警告:oldMethod方法已废弃:请使用newMethod替代
*/
JavaScript Es15
/*
ES15 (ECMAScript 2024) 新特性
发布年份:2024年6月
ES2024 (ES15) 包含以下主要新特性:
1. Promise.withResolvers()
- 简化 Promise 创建,外部控制 resolve/reject
- 提高代码可读性
- 发布年份:2024
2. String.prototype.isWellFormed()
- 检查字符串是否包含格式良好的 Unicode
- 检测无效 UTF-16 代理对
- 发布年份:2024
3. String.prototype.toWellFormed()
- 将无效字符串转换为格式良好的字符串
- 用 U+FFFD 替换无效代理对
- 发布年份:2024
4. 正则表达式 v 标志
- 启用 Unicode 集合语法
- 支持集合操作(交集、并集、差集)
- 增强 Unicode 支持
- 发布年份:2024
5. ArrayBuffer.prototype.resize()
- 动态调整 ArrayBuffer 大小
- 提高内存使用效率
- 发布年份:2024
6. ArrayBuffer.prototype.transfer()
- 转移 ArrayBuffer 数据到新缓冲区
- 避免复制,提高性能
- 发布年份:2024
7. Atomics.waitAsync()
- 异步等待共享内存值变化
- 非阻塞操作,避免主线程阻塞
- 适用于并发编程
- 发布年份:2024
这些特性进一步增强了 JavaScript 语言的功能和性能,提高了开发者的编程体验和代码质量。
*/
// ===============================
// 1. Promise.withResolvers() 方法
// ===============================
// 语法:
// const { promise, resolve, reject } = Promise.withResolvers();
// 使用场景:
// - 简化手动创建 Promise 时管理 resolve 和 reject 的复杂性
// - 替代传统的 new Promise((resolve, reject) => { ... }) 模式
// - 适合需要在 Promise 外部控制 resolve/reject 的场景
// - 提高代码可读性和维护性
// 示例1:基本使用
console.log('=== Promise.withResolvers() 基本使用 ===');
const { promise, resolve, reject } = Promise.withResolvers();
promise.then(result => {
console.log(`Promise 已解决:${result}`);
}).catch(error => {
console.error(`Promise 已拒绝:${error}`);
});
// 在外部控制 Promise 的状态
setTimeout(() => {
resolve('外部解决 Promise');
}, 1000);
// 示例2:替代传统 Promise 创建方式
console.log('\n=== Promise.withResolvers() 与传统方式对比 ===');
// 传统方式
const traditionalPromise = new Promise((resolve, reject) => {
setTimeout(() => resolve('传统方式解决'), 500);
});
// 使用 withResolvers()
const { promise: newPromise, resolve: newResolve } = Promise.withResolvers();
setTimeout(() => newResolve('withResolvers() 方式解决'), 500);
// 比较两种方式的结果
Promise.all([traditionalPromise, newPromise]).then(results => {
console.log(`传统方式结果:${results[0]}`);
console.log(`withResolvers() 结果:${results[1]}`);
});
// 示例3:实际应用场景 - 超时控制
console.log('\n=== Promise.withResolvers() 实际应用:超时控制 ===');
function withTimeout(promise, ms) {
const { promise: timeoutPromise, reject: timeoutReject } = Promise.withResolvers();
const timeoutId = setTimeout(() => timeoutReject(new Error('操作超时')), ms);
return Promise.race([
promise,
timeoutPromise
]).finally(() => clearTimeout(timeoutId));
}
// 使用示例
const longRunningPromise = new Promise(resolve => {
setTimeout(() => resolve('长时间操作完成'), 2000);
});
// 超时时间设为1000ms
withTimeout(longRunningPromise, 1000)
.then(result => console.log(result))
.catch(error => console.error(error.message));
// ===============================
// 2. String.prototype.isWellFormed() 方法
// ===============================
// 语法:
// string.isWellFormed()
// 使用场景:
// - 检查字符串是否包含格式良好的 Unicode
// - 检测是否有无效的 UTF-16 代理对
// - 适合处理来自外部的字符串数据
// - 用于数据验证和清理
// 示例1:基本使用
console.log('\n=== String.prototype.isWellFormed() 基本使用 ===');
// 格式良好的字符串
const wellFormed = 'Hello, World! 👋';
console.log(`${wellFormed} 是格式良好的:${wellFormed.isWellFormed()}`); // 输出: true
// 包含无效代理对的字符串
const badString = 'Hello, \uD800 World!'; // 无效的 UTF-16 代理对
console.log(`${badString} 是格式良好的:${badString.isWellFormed()}`); // 输出: false
// ===============================
// 3. String.prototype.toWellFormed() 方法
// ===============================
// 语法:
// string.toWellFormed()
// 使用场景:
// - 将包含无效 UTF-16 代理对的字符串转换为格式良好的字符串
// - 用 Unicode 替换字符 (U+FFFD) 替换无效代理对
// - 适合清理和修复外部字符串数据
// - 用于确保字符串可以安全处理
// 示例1:基本使用
console.log('\n=== String.prototype.toWellFormed() 基本使用 ===');
// 包含无效代理对的字符串
const malformedString = 'Hello, \uD800 \uDC00 World! \uD800';
console.log(`原始字符串:${malformedString}`);
console.log(`是否格式良好:${malformedString.isWellFormed()}`); // 输出: false
// 转换为格式良好的字符串
const fixedString = malformedString.toWellFormed();
console.log(`修复后的字符串:${fixedString}`);
console.log(`是否格式良好:${fixedString.isWellFormed()}`); // 输出: true
// 示例2:实际应用场景 - 清理用户输入
console.log('\n=== String.prototype.toWellFormed() 实际应用:清理用户输入 ===');
function sanitizeInput(input) {
// 移除无效字符并转换为格式良好的字符串
return input.toWellFormed().trim();
}
// 使用示例
const userInput = ' 用户输入包含无效字符:\uD800 \n';
const sanitizedInput = sanitizeInput(userInput);
console.log(`原始输入:${JSON.stringify(userInput)}`);
console.log(`清理后输入:${JSON.stringify(sanitizedInput)}`);
console.log(`是否格式良好:${sanitizedInput.isWellFormed()}`);
// ===============================
// 4. 正则表达式 v 标志(Unicode Sets)
// ===============================
// 语法:
// /pattern/v
// 使用场景:
// - 启用 Unicode 集合语法
// - 支持更强大的 Unicode 字符类
// - 允许集合的交集、并集和差集
// - 提供更好的 Unicode 支持
// 示例1:基本使用
console.log('\n=== 正则表达式 v 标志基本使用 ===');
// 使用 v 标志创建正则表达式
const regexV = /^[\p{Script=Greek}]+$/v;
console.log(`"Αλφαβητο" 是否匹配希腊字母:${regexV.test('Αλφαβητο')}`); // 输出: true
console.log(`"Hello" 是否匹配希腊字母:${regexV.test('Hello')}`); // 输出: false
// 示例2:Unicode 集合操作
console.log('\n=== 正则表达式 v 标志:Unicode 集合操作 ===');
// 并集操作:匹配元音字母 (a, e, i, o, u) 或数字
const unionRegex = /^[aeiou\d]+$/v;
console.log(`"aeiou" 是否匹配元音或数字:${unionRegex.test('aeiou')}`); // 输出: true
console.log(`"12345" 是否匹配元音或数字:${unionRegex.test('12345')}`); // 输出: true
console.log(`"abc123" 是否匹配元音或数字:${unionRegex.test('abc123')}`); // 输出: false
// 示例3:与 u 标志的对比
console.log('\n=== 正则表达式 v 标志与 u 标志对比 ===');
const regexU = /^\p{L}+$/u;
const regexV = /^\p{L}+$/v;
console.log(`使用 u 标志匹配 "Hello":${regexU.test('Hello')}`); // 输出: true
console.log(`使用 v 标志匹配 "Hello":${regexV.test('Hello')}`); // 输出: true
// v 标志支持更高级的 Unicode 特性
const regexVAdvanced = /^[\p{Letter}&&[^\p{Script=Latin}]]+$/v;
console.log(`"Αλφαβητο" 是否匹配非拉丁字母:${regexVAdvanced.test('Αλφαβητο')}`); // 输出: true
console.log(`"Hello" 是否匹配非拉丁字母:${regexVAdvanced.test('Hello')}`); // 输出: false
// ===============================
// 5. ArrayBuffer.prototype.resize() 方法
// ===============================
// 语法:
// arrayBuffer.resize(newLength)
// 使用场景:
// - 调整 ArrayBuffer 的大小
// - 适合需要动态调整缓冲区大小的场景
// - 用于处理不确定大小的数据
// - 提高内存使用效率
// 示例1:基本使用
console.log('\n=== ArrayBuffer.prototype.resize() 基本使用 ===');
// 创建一个可调整大小的 ArrayBuffer
const buffer = new ArrayBuffer(8, { maxByteLength: 16 });
console.log(`初始缓冲区大小:${buffer.byteLength} 字节`); // 输出: 8
// 调整大小为 12 字节
buffer.resize(12);
console.log(`调整后缓冲区大小:${buffer.byteLength} 字节`); // 输出: 12
// 调整大小为 16 字节(最大值)
buffer.resize(16);
console.log(`再次调整后缓冲区大小:${buffer.byteLength} 字节`); // 输出: 16
// 示例2:写入和读取数据
console.log('\n=== ArrayBuffer.prototype.resize() 读写数据 ===');
const dataBuffer = new ArrayBuffer(4, { maxByteLength: 16 });
const view = new Uint32Array(dataBuffer);
// 写入初始数据
view[0] = 0x12345678;
console.log(`初始数据:0x${view[0].toString(16)}`); // 输出: 0x12345678
// 调整大小并写入更多数据
dataBuffer.resize(8);
const view8 = new Uint32Array(dataBuffer);
view8[1] = 0x87654321;
console.log(`调整后数据[0]:0x${view8[0].toString(16)}`); // 输出: 0x12345678
console.log(`调整后数据[1]:0x${view8[1].toString(16)}`); // 输出: 0x87654321
// ===============================
// 6. ArrayBuffer.prototype.transfer() 方法
// ===============================
// 语法:
// arrayBuffer.transfer(newByteLength)
// arrayBuffer.transfer() // 使用原大小
// 使用场景:
// - 将 ArrayBuffer 的数据转移到新的 ArrayBuffer
// - 避免复制大型缓冲区,提高性能
// - 适合需要转移所有权的场景
// - 用于优化内存使用
// 示例1:基本使用
console.log('\n=== ArrayBuffer.prototype.transfer() 基本使用 ===');
const sourceBuffer = new ArrayBuffer(8);
const sourceView = new Uint32Array(sourceBuffer);
sourceView[0] = 0x11223344;
sourceView[1] = 0x55667788;
console.log(`源缓冲区大小:${sourceBuffer.byteLength} 字节`); // 输出: 8
console.log(`源数据[0]:0x${sourceView[0].toString(16)}`); // 输出: 0x11223344
// 转移数据到新缓冲区
const transferredBuffer = sourceBuffer.transfer(12); // 可以调整大小
console.log(`转移后源缓冲区大小:${sourceBuffer.byteLength} 字节`); // 输出: 0
console.log(`新缓冲区大小:${transferredBuffer.byteLength} 字节`); // 输出: 12
// 读取转移后的数据
const transferredView = new Uint32Array(transferredBuffer);
console.log(`新缓冲区数据[0]:0x${transferredView[0].toString(16)}`); // 输出: 0x11223344
console.log(`新缓冲区数据[1]:0x${transferredView[1].toString(16)}`); // 输出: 0x55667788
// 示例2:实际应用场景 - 数据传输优化
console.log('\n=== ArrayBuffer.prototype.transfer() 实际应用:数据传输优化 ===');
function processLargeData(data) {
// 处理数据...
console.log(`处理了 ${data.byteLength} 字节的数据`);
// 转移数据,避免复制
return data.transfer();
}
// 使用示例
const largeBuffer = new ArrayBuffer(1024 * 1024); // 1MB 缓冲区
console.log(`创建了 ${largeBuffer.byteLength} 字节的缓冲区`);
const processedBuffer = processLargeData(largeBuffer);
console.log(`处理后原始缓冲区大小:${largeBuffer.byteLength} 字节`);
console.log(`处理后新缓冲区大小:${processedBuffer.byteLength} 字节`);
// ===============================
// 7. Atomics.waitAsync() 方法
// ===============================
// 语法:
// Atomics.waitAsync(typedArray, index, value, timeout)
// 使用场景:
// - 异步等待共享内存位置的值变化
// - 避免阻塞主线程
// - 适合并发编程场景
// - 用于 Web Workers 之间的通信
// 示例1:基本使用(概念演示)
console.log('\n=== Atomics.waitAsync() 基本使用 ===');
// 创建一个共享的 ArrayBuffer
const sharedBuffer = new SharedArrayBuffer(4);
const sharedView = new Int32Array(sharedBuffer);
// 初始值设为 0
Atomics.store(sharedView, 0, 0);
// 异步等待值变化
const waitResult = Atomics.waitAsync(sharedView, 0, 0, 1000);
console.log(`等待状态:${waitResult.async ? '异步' : '同步'}`);
// 在另一个上下文(如 Worker)中修改值
setTimeout(() => {
Atomics.store(sharedView, 0, 1);
Atomics.notify(sharedView, 0);
console.log('已修改共享内存的值并通知等待者');
}, 500);
// 处理等待结果
waitResult.value.then(result => {
console.log(`等待结果:${result}`); // 输出: ok
}).catch(error => {
console.error(`等待出错:${error}`);
});
// 示例2:实际应用场景 - 非阻塞等待
console.log('\n=== Atomics.waitAsync() 实际应用:非阻塞等待 ===');
// 模拟一个需要等待共享资源的场景
function waitForResource(resource, expectedValue, timeout) {
return Atomics.waitAsync(resource, 0, expectedValue, timeout).value;
}
// 使用示例
const resourceBuffer = new SharedArrayBuffer(4);
const resourceView = new Int32Array(resourceBuffer);
Atomics.store(resourceView, 0, 0);
console.log('开始等待资源可用...');
// 非阻塞等待
waitForResource(resourceView, 0, 2000).then(result => {
console.log(`资源等待结果:${result}`);
if (result === 'ok') {
console.log('资源已可用,开始处理...');
} else {
console.log('资源等待超时');
}
});
// 模拟资源被释放
setTimeout(() => {
Atomics.store(resourceView, 0, 1);
Atomics.notify(resourceView, 0);
console.log('资源已释放');
}, 1000);