主题
JavaScript 高级用法(ES6+)
本文档涵盖了 JavaScript ES6+ 的高级特性,每个特性都包含详细说明、代码示例和使用场景。
目录
核心语法
let 与 const
说明
ES6 引入了块级作用域变量声明方式,替代传统的 var。
| 关键字 | 作用域 | 是否可重新赋值 | 是否必须初始化 |
|---|---|---|---|
var | 函数作用域 | 是 | 否 |
let | 块级作用域 | 是 | 否 |
const | 块级作用域 | 否 | 是 |
代码示例
javascript
// let - 可重新赋值
let count = 0;
count = 1; // 正确
// const - 不可重新赋值(但对象属性可修改)
const PI = 3.14159;
PI = 3.14; // TypeError: Assignment to constant variable
const config = { port: 3000 };
config.port = 8080; // 正确!对象引用未改变,只是修改属性
config = {}; // TypeError
// 块级作用域示例
{
let blockScoped = '块级变量';
var functionScoped = '函数级变量';
}
console.log(blockScoped); // ReferenceError
console.log(functionScoped); // 正确
// 暂时性死区(TDZ)
console.log(myVar); // undefined(var声明提升)
// console.log(myLet); // ReferenceError(let/const存在TDZ)
let myLet = 10;使用场景
- 使用
const:默认首选,用于不会重新赋值的变量(配置、常量、导入的模块) - 使用
let:需要重新赋值的循环变量、计数器 - 避免
var:除非必须兼容老版本浏览器,否则应避免使用
箭头函数(Arrow Functions)
说明
更简洁的函数语法,且不绑定自己的 this、arguments、super 或 new.target。
代码示例
javascript
// 基础语法
const add = (a, b) => a + b;
const square = x => x * x;
const greet = () => 'Hello';
// 与高阶函数配合使用
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(x => x * 2); // [2, 4, 6, 8, 10]
// 对象字面量返回(需要括号包裹)
const createUser = (name, age) => ({ name, age });
// this 绑定问题
const obj = {
count: 0,
// 普通函数:this 指向调用者
normalFunc: function() {
setTimeout(function() {
console.log(this.count); // undefined(this指向window或undefined)
}, 100);
},
// 箭头函数:this 继承外层
arrowFunc: function() {
setTimeout(() => {
console.log(this.count); // 正确获取到 obj.count
}, 100);
}
};使用场景
- 推荐使用:数组方法回调、Promise链、简短的工具函数
- 避免使用:
- 对象方法(使用简写方法语法)
- 需要使用
arguments对象的场景 - 作为构造函数(箭头函数不能使用
new) - 需要动态
this的场景(如事件处理器)
javascript
// 对象方法应该用简写语法(不用箭头函数)
const calculator = {
sum(a, b) { // 而不是 sum: (a, b) => {}
return a + b;
}
};
// 事件处理器需要动态 this
button.addEventListener('click', function() {
this.classList.toggle('active'); // this指向button元素
});模板字符串(Template Literals)
说明
使用反引号包裹的字符串,支持多行、插值表达式和标签模板。
代码示例
javascript
// 基础插值
const name = '张三';
const greeting = `你好,${name}!`; // "你好,张三!"
// 多行字符串
const html = `
<div class="container">
<h1>${greeting}</h1>
<p>这是一个段落</p>
</div>
`;
// 表达式求值
const a = 10, b = 20;
const result = `${a} + ${b} = ${a + b}`; // "10 + 20 = 30"
// 嵌套模板
const user = { name: '李四', role: 'admin' };
const badge = `
<div class="user-badge user-${user.role}">
<span class="name">${user.name}</span>
<span class="role">${user.role === 'admin' ? '管理员' : '普通用户'}</span>
</div>
`;
// 标签模板(高级用法)
function highlight(strings, ...values) {
return strings.reduce((result, string, i) => {
const value = values[i] !== undefined ? `<mark>${values[i]}</mark>` : '';
return result + string + value;
}, '');
}
const mood = 'happy';
const message = highlight`I'm feeling ${mood} today!`;
// "I'm feeling <mark>happy</mark> today!"使用场景
- HTML模板生成
- SQL查询构建
- 日志消息格式化
- 多行文本
- 任何需要字符串插值的场景
解构赋值(Destructuring)
说明
从数组或对象中提取值,按照对应位置或属性名赋值给变量。
数组解构
javascript
// 基础解构
const [a, b, c] = [1, 2, 3];
console.log(a, b, c); // 1 2 3
// 跳过元素
const [first, , third] = [1, 2, 3];
console.log(first, third); // 1 3
// 默认值
const [x, y, z = 3] = [1, 2];
console.log(x, y, z); // 1 2 3
// 剩余元素
const [head, ...tail] = [1, 2, 3, 4];
console.log(head); // 1
console.log(tail); // [2, 3, 4]
// 交换变量
let m = 1, n = 2;
[m, n] = [n, m];
console.log(m, n); // 2 1
// 解析函数返回值
function getCoordinates() {
return [12.34, 56.78];
}
const [lat, lng] = getCoordinates();对象解构
javascript
// 基础解构
const { name, age } = { name: '张三', age: 30, city: '北京' };
console.log(name, age); // "张三" 30
// 重命名
const { name: userName, age: userAge } = { name: '张三', age: 30 };
console.log(userName, userAge); // "张三" 30
// 默认值
const { x, y = 20 } = { x: 10 };
console.log(x, y); // 10 20
// 剩余属性
const { a, ...rest } = { a: 1, b: 2, c: 3 };
console.log(rest); // { b: 2, c: 3 }
// 嵌套解构
const user = {
info: {
name: '李四',
address: {
city: '北京',
district: '朝阳'
}
}
};
const { info: { name, address: { city } } } = user;
console.log(name, city); // "李四" "北京"
// 函数参数解构
function createUser({ name, age = 18, role = 'user' }) {
return { name, age, role };
}
createUser({ name: '王五' }); // { name: '王五', age: 18, role: 'user' }使用场景
- 提取API响应数据
- 函数参数处理
- 交换变量值
- 导入模块的特定导出
- 处理配置对象
javascript
// API响应处理
async function fetchUser() {
const response = await fetch('/api/user');
const { data: { name, email, avatar }, status } = await response.json();
return { name, email, avatar, status };
}
// Vue组件 props解构
const props = defineProps({
user: Object,
title: String
});
const { user, title } = toRefs(props);扩展运算符与剩余参数(Spread & Rest)
说明
... 语法根据上下文有两种用途:展开(Spread)和收集(Rest)。
扩展运算符(Spread)
javascript
// 数组展开
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]
// 数组复制(浅拷贝)
const original = [1, 2, 3];
const copy = [...original];
// 数组前面插入元素
const newArr = [0, ...original]; // [0, 1, 2, 3]
// 对象展开
const defaults = { theme: 'light', lang: 'zh' };
const userPrefs = { theme: 'dark' };
const settings = { ...defaults, ...userPrefs }; // { theme: 'dark', lang: 'zh' }
// 对象复制(浅拷贝)
const originalObj = { a: 1, b: 2 };
const copyObj = { ...originalObj };
// 对象属性更新(React/Vue 状态更新)
const state = { count: 0, name: 'test' };
const newState = { ...state, count: state.count + 1 }; // { count: 1, name: 'test' }剩余参数(Rest)
javascript
// 函数参数收集
function sum(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
sum(1, 2, 3, 4); // 10
function log(type, ...messages) {
console.log(`[${type}]`, ...messages);
}
log('INFO', '用户登录', 'userID: 123');
// 数组解构剩余元素
const [first, second, ...rest] = [1, 2, 3, 4, 5];
console.log(rest); // [3, 4, 5]
// 对象解构剩余属性
const { name, ...others } = { name: '张三', age: 30, city: '北京' };
console.log(others); // { age: 30, city: '北京' }使用场景
- 数组:合并、复制、插入元素、函数调用传参
- 对象:合并、复制、更新属性、提取特定属性
- 函数:收集可变参数、解构参数提取
javascript
// React/Vue 常用场景
// 合并 state
const newState = { ...oldState, isLoading: true };
// 提取特定 props
const { data, loading, ...restProps } = props;对象属性简写
说明
当属性名与变量名相同时,可以简写。
代码示例
javascript
const name = '张三';
const age = 30;
const city = '北京';
// 完整写法
const user1 = {
name: name,
age: age,
city: city
};
// 简写(属性名与变量名相同)
const user2 = { name, age, city };
// 方法简写
const obj = {
// 完整写法
sayHello1: function() {
return 'Hello';
},
// 简写
sayHello2() {
return 'Hello';
},
// 箭头函数(注意 this 绑定不同)
sayHello3: () => 'Hello'
};
// 计算属性名
const prop = 'dynamic';
const obj2 = {
[prop]: 'value',
[`get${prop.charAt(0).toUpperCase() + prop.slice(1)}`]() {
return this[prop];
}
};使用场景
- 构建配置对象
- 返回对象字面量
- Vue组件的 data、methods、computed
对象与数组高级用法
对象静态方法
Object.assign()
javascript
// 合并对象(浅拷贝)
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
console.log(target); // { a: 1, b: 2, c: 3 }
// 覆盖默认配置
const defaults = { theme: 'light', lang: 'zh' };
const userConfig = { theme: 'dark' };
const config = Object.assign({}, defaults, userConfig);
// 注意:只能浅拷贝
const original = { nested: { value: 1 } };
const copy = Object.assign({}, original);
copy.nested.value = 2;
console.log(original.nested.value); // 2(被影响了!)Object.keys() / Object.values() / Object.entries()
javascript
const user = {
name: '张三',
age: 30,
city: '北京'
};
// 获取所有键
Object.keys(user); // ['name', 'age', 'city']
// 获取所有值
Object.values(user); // ['张三', 30, '北京']
// 获取键值对数组
Object.entries(user); // [['name', '张三'], ['age', 30], ['city', '北京']]
// 遍历对象
Object.entries(user).forEach(([key, value]) => {
console.log(`${key}: ${value}`);
});
// Object.fromEntries() 反向操作
const entries = [['name', '李四'], ['age', 30]];
const obj = Object.fromEntries(entries); // { name: '李四', age: 30 }Object.freeze() / Object.seal()
javascript
// Object.freeze() - 冻结对象(不可修改、删除、添加属性)
const frozen = Object.freeze({ a: 1 });
frozen.a = 2; // 静默失败(严格模式报错)
frozen.b = 3; // 静默失败
delete frozen.a; // 静默失败
// Object.seal() - 密封对象(不可删除、添加,但可修改)
const sealed = Object.seal({ a: 1 });
sealed.a = 2; // 成功
sealed.b = 3; // 静默失败
delete sealed.a; // 静默失败数组高级方法
Array.prototype.flat() / flatMap()
javascript
// flat() - 数组扁平化
const nested = [1, [2, [3, [4, 5]]]];
nested.flat(); // [1, 2, [3, [4, 5]]]
nested.flat(2); // [1, 2, 3, [4, 5]]
nested.flat(Infinity); // [1, 2, 3, 4, 5]
// flatMap() - 映射后扁平化一层
const sentences = ['Hello World', 'How are you'];
const words = sentences.flatMap(sentence => sentence.split(' '));
// ['Hello', 'World', 'How', 'are', 'you']
// 相当于 map + flat(1)Array.prototype.find() / findIndex() / findLast() / findLastIndex()
javascript
const users = [
{ id: 1, name: '张三', role: 'admin' },
{ id: 2, name: '李四', role: 'user' },
{ id: 3, name: '王五', role: 'admin' }
];
// find() - 找到第一个符合条件的元素
const admin = users.find(u => u.role === 'admin');
// { id: 1, name: '张三', role: 'admin' }
// findIndex() - 找到第一个符合条件的索引
const adminIndex = users.findIndex(u => u.role === 'admin');
// 0
// findLast() - 找到最后一个符合条件的元素(ES2023)
const lastAdmin = users.findLast(u => u.role === 'admin');
// { id: 3, name: '王五', role: 'admin' }
// findLastIndex() - 找到最后一个符合条件的索引(ES2023)
const lastAdminIndex = users.findLastIndex(u => u.role === 'admin');
// 2Array.prototype.at()
javascript
const arr = [10, 20, 30, 40, 50];
// 正向索引
arr.at(0); // 10
arr.at(2); // 30
// 负向索引(从末尾开始)
arr.at(-1); // 50(最后一个元素)
arr.at(-2); // 40(倒数第二个)
// 等价于
arr[arr.length - 1]; // 50Array.prototype.toSorted() / toReversed() / toSpliced() / with()
javascript
const numbers = [3, 1, 4, 1, 5];
// 这些方法返回新数组,不修改原数组(ES2023)
const sorted = numbers.toSorted((a, b) => a - b);
// [1, 1, 3, 4, 5]
console.log(numbers); // [3, 1, 4, 1, 5](原数组未变)
const reversed = numbers.toReversed();
// [5, 1, 4, 1, 3]
// with(index, value) - 替换指定索引的元素
const replaced = numbers.with(2, 99);
// [3, 1, 99, 1, 5]
// toSpliced(start, deleteCount, ...items) - 删除/插入元素
const spliced = numbers.toSpliced(2, 1, 100);
// [3, 1, 100, 1, 5]异步编程
Promise 基础
说明
Promise 是异步操作的容器,表示一个尚未完成但预期会完成的操作。
三种状态
| 状态 | 说明 | 是否可变 |
|---|---|---|
pending | 进行中 | 是 |
fulfilled | 已成功 | 否(终态) |
rejected | 已失败 | 否(终态) |
代码示例
javascript
// 创建 Promise
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const success = true;
if (success) {
resolve('操作成功');
} else {
reject(new Error('操作失败'));
}
}, 1000);
});
// 使用 Promise
promise
.then(result => console.log(result))
.catch(error => console.error(error))
.finally(() => console.log('无论成功失败都执行'));
// Promise.resolve() / Promise.reject()
const resolved = Promise.resolve('立即成功');
const rejected = Promise.reject(new Error('立即失败'));
// Promise 作为值
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
async function demo() {
console.log('开始');
await wait(1000);
console.log('1秒后');
}Promise.all()
说明
并发执行多个 Promise,全部成功才成功,有一个失败立即失败。
代码示例
javascript
// 场景:同时加载多个接口,必须都成功
async function init() {
showLoadingToast({ message: '加载中...', forbidClick: true });
try {
// 并发请求,必须都成功
const [questionListRes, dictRes] = await Promise.all([
request('/api/questions'),
request('/api/dict')
]);
data.listContent = questionListRes?.data || [];
data.listSelection = dictRes?.data || [];
} catch (e) {
console.error('请求失败:', e);
} finally {
closeToast();
}
}使用场景
- 需要同时加载多个数据源
- 所有请求都成功才能继续操作
- 利用并行提高性能
Promise.allSettled()
说明
并发执行多个 Promise,无论成功失败都返回结果。
代码示例
javascript
async function init() {
showLoadingToast({ message: '加载中...', forbidClick: true });
try {
const results = await Promise.allSettled([
request('/api/questions'),
request('/api/dict')
]);
// 单独处理每个结果
if (results[0].status === 'fulfilled') {
data.listContent = results[0].value?.data || [];
} else {
console.error('获取题目失败:', results[0].reason);
}
if (results[1].status === 'fulfilled') {
data.listSelection = results[1].value?.data || [];
} else {
console.error('获取字典失败:', results[1].reason);
}
} catch (e) {
console.error('未知错误:', e);
} finally {
closeToast();
}
}返回格式
javascript
// 成功的结果
{ status: 'fulfilled', value: {...} }
// 失败的结果
{ status: 'rejected', reason: Error }使用场景
- 需要获取所有请求的结果
- 某些请求失败不影响其他结果的处理
- 需要显示每个请求的成功/失败状态
Promise.race()
说明
返回最快完成(无论成功或失败)的 Promise 结果。
代码示例
javascript
// 超时控制
function fetchWithTimeout(url, timeout = 5000) {
const fetchPromise = fetch(url);
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error('请求超时')), timeout)
);
return Promise.race([fetchPromise, timeoutPromise]);
}
// 多源请求,取最快的
const result = await Promise.race([
fetchFromSource1(),
fetchFromSource2(),
fetchFromBackup()
]);使用场景
- 请求超时控制
- 多源数据竞争
- 获取最快响应的结果
Promise.any()
说明
返回第一个成功完成的 Promise,全部失败才失败。
代码示例
javascript
// 从多个镜像源下载,只要有一个成功即可
async function downloadFromMirrors(file) {
const mirrors = [
`https://mirror1.com/${file}`,
`https://mirror2.com/${file}`,
`https://mirror3.com/${file}`
];
try {
const response = await Promise.any(
mirrors.map(url => fetch(url))
);
return response;
} catch (error) {
// AggregateError: 所有请求都失败
console.error('所有镜像源都不可用');
}
}使用场景
- 从多个备用资源源获取数据
- 只要有一个成功即可的操作
- 容错场景
async/await
说明
基于 Promise 的语法糖,让异步代码看起来像同步代码。
代码示例
javascript
// async 函数总是返回 Promise
async function fetchData() {
return '数据'; // 等价于 return Promise.resolve('数据')
}
// await 等待 Promise 结果
async function getUser(id) {
const response = await fetch(`/api/users/${id}`);
const user = await response.json();
return user;
}
// 错误处理
async function loadUser(id) {
try {
const user = await getUser(id);
console.log(user);
} catch (error) {
console.error('加载失败:', error);
} finally {
console.log('加载结束');
}
}
// 并行执行(推荐使用 Promise.all)
async function loadMultiple() {
// 错误:串行执行(慢)
const user1 = await getUser(1);
const user2 = await getUser(2);
// 正确:并行执行(快)
const [user1, user2] = await Promise.all([
getUser(1),
getUser(2)
]);
}
// 顺序执行(确实需要按顺序的场景)
async function processItems(items) {
const results = [];
for (const item of items) {
const result = await processItem(item);
results.push(result);
}
return results;
}
// for...of + await(顺序处理异步任务)
async function downloadImages(urls) {
for (const url of urls) {
await downloadImage(url);
console.log(`下载完成: ${url}`);
}
}
// Promise.all() 并行处理
async function downloadImagesParallel(urls) {
await Promise.all(urls.map(url => downloadImage(url)));
console.log('全部下载完成');
}使用场景
- 任何异步操作(API请求、文件操作、数据库查询等)
- 需要顺序执行的异步操作
- 需要错误处理的异步操作
最佳实践
javascript
// 1. 总是使用 try-catch 处理错误
async function safeRequest(url) {
try {
const response = await fetch(url);
return await response.json();
} catch (error) {
console.error(`请求失败: ${url}`, error);
throw error; // 或者返回默认值
}
}
// 2. 并行执行独立请求
async function loadDashboard() {
const [user, posts, notifications] = await Promise.all([
fetchUser(),
fetchPosts(),
fetchNotifications()
]);
return { user, posts, notifications };
}
// 3. 使用 Promise.allSettled 容错
async function loadWithTolerance() {
const results = await Promise.allSettled([
fetchPrimary(),
fetchSecondary()
]);
return results
.filter(r => r.status === 'fulfilled')
.map(r => r.value);
}集合与数据结构
Map
说明
键值对集合,键可以是任意类型(包括对象)。
代码示例
javascript
// 创建 Map
const map = new Map();
// 增删改查
map.set('name', '张三');
map.set(123, '数字键');
map.set({ id: 1 }, '对象键');
map.get('name'); // '张三'
map.has('name'); // true
map.delete('name');
map.clear();
// 遍历
map.forEach((value, key) => console.log(key, value));
for (const [key, value] of map) {
console.log(key, value);
}
// 初始化
const config = new Map([
['timeout', 5000],
['retries', 3],
['debug', true]
]);
// 常用方法
const keys = map.keys();
const values = map.values();
const entries = map.entries();
const size = map.size;
// Object vs Map
// 1. Map 的键可以是任意类型,Object 只能是字符串/Symbol
// 2. Map 有 size 属性,Object 需要 Object.keys().length
// 3. Map 是有序的,Object 在某些情况下无序
// 4. Map 频繁增删性能更好使用场景
- 需要非字符串作为键
- 频繁增删键值对
- 需要保持插入顺序
- 缓存实现
javascript
// DOM 节点缓存
const nodeData = new Map();
function setData(node, data) {
nodeData.set(node, data);
}
// 路由参数缓存
const routeCache = new Map();
function getRouteParams(path) {
if (!routeCache.has(path)) {
routeCache.set(path, parseRoute(path));
}
return routeCache.get(path);
}Set
说明
值的集合,值唯一(自动去重)。
代码示例
javascript
// 创建 Set
const set = new Set([1, 2, 2, 3, 3, 3]);
console.log(set); // Set {1, 2, 3}
// 增删查
set.add(4);
set.has(2); // true
set.delete(2);
set.clear();
// 数组去重
const arr = [1, 2, 2, 3, 3, 3];
const uniqueArr = [...new Set(arr)]; // [1, 2, 3]
// 字符串去重
const str = 'hello';
const uniqueChars = [...new Set(str)].join(''); // 'helo'
// 集合运算
const a = new Set([1, 2, 3]);
const b = new Set([2, 3, 4]);
// 并集
const union = new Set([...a, ...b]); // Set {1, 2, 3, 4}
// 交集
const intersection = new Set([...a].filter(x => b.has(x)));
// Set {2, 3}
// 差集
const difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}使用场景
- 数组去重
- 存储唯一值集合
- 集合运算(并集、交集、差集)
WeakMap & WeakSet
说明
弱引用版本,键必须是对象,垃圾回收友好。
代码示例
javascript
// WeakMap - 键必须是对象,弱引用
const weakMap = new WeakMap();
const obj = { id: 1 };
weakMap.set(obj, '元数据');
// 当 obj 没有其他引用时,会被垃圾回收
// WeakMap 中的条目自动删除
obj = null;
// WeakSet - 值必须是对象
const weakSet = new WeakSet();
const obj1 = { name: 'obj1' };
const obj2 = { name: 'obj2' };
weakSet.add(obj1);
weakSet.add(obj2);
weakSet.has(obj1); // true使用场景
- WeakMap:存储对象私有数据、DOM 节点关联数据
- WeakSet:跟踪对象活动状态、标记对象
javascript
// 私有数据存储
const privateData = new WeakMap();
class User {
constructor(name, password) {
this.name = name;
privateData.set(this, { password });
}
checkPassword(input) {
return privateData.get(this).password === input;
}
}函数进阶
默认参数
说明
为函数参数设置默认值。
代码示例
javascript
// 基础用法
function greet(name = '朋友', message = '你好') {
return `${message},${name}!`;
}
greet(); // '你好,朋友!'
greet('张三'); // '你好,张三!'
greet('张三', '欢迎'); // '欢迎,张三!'
// 解构 + 默认值
function createUser({ name = '匿名', age = 18, role = 'user' } = {}) {
return { name, age, role };
}
createUser(); // { name: '匿名', age: 18, role: 'user' }
createUser({ name: '李四' }); // { name: '李四', age: 18, role: 'user' }
// 注意:默认值只对 undefined 有效
function test(a = 1, b = 2) {
return [a, b];
}
test(undefined, null); // [1, null]
test(0, ''); // [0, '']剩余参数与展开运算符
已在 扩展运算符与剩余参数 章节详细说明。
函数属性与方法
代码示例
javascript
// 函数名
function myFunction() {}
myFunction.name; // 'myFunction'
const fn = function() {};
fn.name; // 'fn'
// 函数长度(参数个数)
function func(a, b, c) {}
func.length; // 3
// 函数调用
function greet(greeting, punctuation) {
return `${greeting}, ${this.name}${punctuation}`;
}
const person = { name: '张三' };
greet.call(person, '你好', '!'); // '你好, 张三!'
greet.apply(person, ['你好', '!']); // '你好, 张三!'
const boundGreet = greet.bind(person, '你好');
boundGreet('!'); // '你好, 张三!'类与面向对象
类基础
代码示例
javascript
// 类声明
class Person {
// 实例属性
name = '未命名';
// 私有属性(ES2022)
#password = '';
// 静态属性
static species = '人类';
// 构造函数
constructor(name, password) {
this.name = name;
this.#password = password;
}
// 实例方法
sayHello() {
return `你好,我是${this.name}`;
}
// 私有方法
#validatePassword(input) {
return input === this.#password;
}
// 静态方法
static createAnonymous() {
return new Person('匿名', '123456');
}
// Getter
get info() {
return `${this.name}(${Person.species})`;
}
// Setter
set name(value) {
if (value.length < 2) {
throw new Error('名字太短');
}
this._name = value;
}
get name() {
return this._name;
}
}
// 使用
const person = new Person('张三', 'password');
console.log(person.sayHello()); // '你好,我是张三'
console.log(Person.species); // '人类'
const anonymous = Person.createAnonymous();继承
代码示例
javascript
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return `${this.name}发出声音`;
}
eat() {
return `${this.name}在吃东西`;
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // 调用父类构造函数
this.breed = breed;
}
speak() {
return `${this.name}汪汪叫`;
}
// 重写父类方法但保留部分行为
eat() {
return `${super.eat()},吃得很快`;
}
}
const dog = new Dog('旺财', '哈士奇');
console.log(dog.speak()); // '旺财汪汪叫'
console.log(dog.eat()); // '旺财在吃东西,吃得很快'ES2022+ 类新特性
javascript
// 私有字段(#)
class Counter {
#count = 0;
increment() {
this.#count++;
}
get count() {
return this.#count;
}
}
// 静态初始化块
class Utils {
static config = {};
static {
// 在类定义时执行一次
this.config.debug = true;
this.config.version = '1.0.0';
}
}
// 私有静态字段
class Logger {
static #instance = null;
static getInstance() {
if (!Logger.#instance) {
Logger.#instance = new Logger();
}
return Logger.#instance;
}
}ES2020+ 新特性
可选链操作符(Optional Chaining)?.
说明
安全访问嵌套对象属性,避免 Cannot read property 'xxx' of undefined 错误。
代码示例
javascript
const user = {
profile: {
name: '张三',
address: {
city: '北京'
}
}
};
// 传统写法(繁琐)
const city = user && user.profile && user.profile.address && user.profile.address.city;
// 可选链(简洁)
const city2 = user?.profile?.address?.city;
// 配合 Nullish Coalescing
const city3 = user?.profile?.address?.city ?? '未知';
// 数组可选链
const items = [{ name: 'item1' }, null, { name: 'item3' }];
items?.[0]?.name; // 'item1'
items?.[1]?.name; // undefined
// 函数调用可选链
const result = obj.method?.();
// 使用场景
function getConfig(config) {
return {
api: config?.api?.baseUrl ?? 'https://api.example.com',
timeout: config?.api?.timeout ?? 5000,
retries: config?.api?.retries ?? 3
};
}使用场景
- 访问可能不存在的嵌套属性
- 调用可能不存在的方法
- 处理 API 响应中的可选字段
空值合并操作符(Nullish Coalescing)??
说明
当左侧是 null 或 undefined 时返回右侧值,否则返回左侧值。
与 || 的区别
javascript
// || 会将 0、''、false 视为假值
const count = 0;
const display = count || 10; // 10(可能不是你想要的)
// ?? 只将 null 和 undefined 视为空值
const display2 = count ?? 10; // 0(保留 0)
// 对比示例
const values = [0, '', false, null, undefined];
values.forEach(value => {
console.log(`${value} || 'default':`, value || 'default');
console.log(`${value} ?? 'default':`, value ?? 'default');
});使用场景
- 提供默认值
- 保留
0、''、false等有效值
逻辑赋值操作符
javascript
// ||= (仅在左侧为假值时赋值)
let a = 0;
a ||= 10; // a = 0(保留 0)
let b = null;
b ||= 10; // b = 10
// ??= (仅在左侧为 null/undefined 时赋值)
let c = 0;
c ??= 10; // c = 0
let d = null;
d ??= 10; // d = 10
// &&= (仅在左侧为真值时赋值)
let e = 5;
e &&= 10; // e = 10
let f = 0;
f &&= 10; // f = 0
// 实际应用
function updateOptions(options) {
options ??= {};
options.theme ??= 'light';
options.lang ??= 'zh';
return options;
}数字分隔符(Numeric Separators)
javascript
// 提高数字可读性
const billion = 1_000_000_000;
const bytes = 0xff_ff_ff_ff;
const bits = 0b1010_0001_1000_0101;
// 科学计数法
const avogadro = 6.022_140_76e23;
// 不能连续使用多个分隔符
// const invalid = 123__456; // SyntaxError
// 不能在末尾使用
// const invalid = 123_; // SyntaxErrorString.prototype.replaceAll()
javascript
const text = 'hello world, hello universe';
// 替换所有匹配(全局替换)
const newText = text.replaceAll('hello', 'hi');
// 'hi world, hi universe'
// 等价于(但更简洁)
// text.replace(/hello/g, 'hi')
// 配合正则
const html = '<div>text</div><span>more</span>';
const clean = html.replaceAll(/<[^>]*>/g, '');
// 'textmore'Object.hasOwn()
javascript
const obj = Object.create({ inherited: true });
obj.own = true;
// hasOwnProperty 的问题
obj.hasOwnProperty('own'); // true
obj.hasOwnProperty('inherited'); // false
Object.create(null).hasOwnProperty('key'); // TypeError
// Object.hasOwn() 更安全
Object.hasOwn(obj, 'own'); // true
Object.hasOwn(obj, 'inherited'); // false
Object.hasOwn(Object.create(null), 'key'); // falseArray.prototype.at() / toReversed() / toSorted() / with()
已在 数组高级方法 章节详细说明。
事件循环与执行机制
宏任务与微任务
说明
JavaScript 是单线程的,通过事件循环处理异步任务。
任务分类
| 类型 | 说明 | 举例 |
|---|---|---|
| 同步任务 | 立即执行 | 函数调用、表达式计算 |
| 宏任务(Macro Task) | 事件循环队列 | setTimeout、setInterval、I/O、UI渲染 |
| 微任务(Micro Task) | 当前任务后立即执行 | Promise.then、queueMicrotask、MutationObserver |
执行顺序
javascript
console.log('1. 同步任务开始');
setTimeout(() => {
console.log('2. 宏任务:setTimeout');
}, 0);
Promise.resolve()
.then(() => console.log('3. 微任务:Promise 1'))
.then(() => console.log('4. 微任务:Promise 2'));
console.log('5. 同步任务结束');
// 输出顺序:
// 1. 同步任务开始
// 5. 同步任务结束
// 3. 微任务:Promise 1
// 4. 微任务:Promise 2
// 2. 宏任务:setTimeout执行机制
1. 执行所有同步任务
2. 清空微任务队列
3. 执行一个宏任务
4. 回到步骤2复杂示例
javascript
console.log('start');
setTimeout(() => {
console.log('timeout1');
Promise.resolve().then(() => console.log('promise1'));
}, 0);
Promise.resolve().then(() => {
console.log('promise2');
setTimeout(() => console.log('timeout2'), 0);
});
Promise.resolve().then(() => console.log('promise3'));
console.log('end');
// 输出:
// start
// end
// promise2
// promise3
// timeout1
// promise1
// timeout2使用场景
- 理解异步代码执行顺序
- 排查 Promise 与 setTimeout 混用的问题
- 优化性能(将大任务拆分为微任务)
queueMicrotask()
javascript
// 创建微任务的显式方法
queueMicrotask(() => {
console.log('这是一个微任务');
});
// 等价于
Promise.resolve().then(() => {
console.log('这是一个微任务');
});
// 使用场景:在状态更新后批量处理
function batchUpdate(items) {
items.forEach(item => queueMicrotask(() => processItem(item)));
}实用技巧
Infinity 的妙用
说明
Infinity 是一个特殊的数值,表示无穷大,可用于比较场景。
代码示例
typescript
// 版本比较:不在 map 中的版本号视为最新
const getSupport = async () => {
try {
const v = await getMztAppVersion();
const minVersionMap: Record<string, number> = {
'1': 484,
'2': 133,
'3': 491,
};
// 如果不在 map 中则视为最新版本,使用 Infinity 进行比对
return Number(v.version) >= (minVersionMap[v.type] || Infinity);
} catch (e) {
return false;
}
};
// 数组查找最小值(兼容空数组)
function findMin(numbers: number[]) {
return numbers.reduce((min, num) => Math.min(min, num), Infinity);
}
findMin([3, 1, 4]); // 1
findMin([]); // Infinity动态插入脚本
说明
运行时动态加载外部脚本。
代码示例
javascript
// 方法一:创建 script 标签
function loadScript(src) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = src;
script.async = true;
script.onload = () => {
console.log(`${src} 加载完成`);
resolve();
};
script.onerror = () => {
reject(new Error(`加载失败: ${src}`));
};
document.head.appendChild(script);
});
}
// 使用
loadScript('/libs/b.js')
.then(() => console.log('脚本就绪'))
.catch(err => console.error(err));
// 方法二:动态 import(模块方式)
import('/libs/b.js')
.then(module => {
console.log('模块加载完成', module);
})
.catch(err => {
console.error('模块加载失败', err);
});使用场景
- 按需加载第三方库
- 条件加载不同环境的脚本
- 动态加载插件
JSDoc 文档注释
说明
为代码添加类型和文档信息,提高代码可读性和IDE提示。
代码示例
javascript
/**
* 处理用户信息
*
* @description
* 这是一个示例函数,用于演示 JSDoc 常用标签的写法,包括参数、返回值、
* 类型定义、示例代码、错误、作者信息、引用、版本、弃用等。
*
* @param {Object} [user={}] - 用户信息对象(可选,默认空对象)
* @param {string} [user.name='匿名'] - 用户姓名(可选,默认"匿名")
* @param {number} [user.age=18] - 用户年龄(可选,默认 18)
* @param {Array<string>} [user.tags=[]] - 用户标签(可选,默认空数组)
* @param {(string|number)} [id=null] - 用户 ID,可传字符串或数字(可选,默认 null)
* @param {'blue'|'red'} [color='blue'] - 用户颜色主题,只能选择 'blue' 或 'red'(可选,默认 'blue')
* @param {...string} [roles] - 用户角色,可变参数(可选)
*
* @returns {Promise<Object>} 返回一个 Promise,resolve 用户详情
*
* @throws {TypeError} 当参数格式不正确时抛出
* @deprecated 此方法将在 v2.0 移除,请使用 `fetchUserDetail` 替代
* @author 张三 <zhangsan@example.com>
* @version 1.2.3
* @since 1.0.0
* @see {@link https://jsdoc.app/ JSDoc 官方文档}
*
* @example
* // 调用示例:使用默认值
* getUserDetail()
* .then(user => console.log(user)) // 输出默认的匿名用户
*
* @example
* // 调用示例:传递完整参数
* getUserDetail({name: '李四', age: 20}, 1001, 'admin', 'editor', 'red')
* .then(user => console.log(user))
* .catch(err => console.error(err))
*/
async function getUserDetail(
user = { name: '匿名', age: 18, tags: [] },
id = null,
color = 'red',
...roles
) {
if (typeof user !== 'object') {
throw new TypeError('参数 user 必须是对象');
}
if (!['blue', 'red'].includes(color)) {
throw new TypeError('color 必须是 "blue" 或 "red"');
}
return Promise.resolve({
...user,
id,
roles,
color,
});
}常用标签
| 标签 | 说明 |
|---|---|
@param | 参数说明 |
@returns | 返回值说明 |
@throws | 可能抛出的错误 |
@example | 使用示例 |
@deprecated | 弃用标记 |
@see | 相关链接 |
@since | 起始版本 |
@author | 作者信息 |
@version | 版本号 |
并发请求控制
javascript
// 限制并发数量的请求池
class RequestPool {
constructor(maxConcurrent = 5) {
this.maxConcurrent = maxConcurrent;
this.current = 0;
this.queue = [];
}
async run(fn) {
while (this.current >= this.maxConcurrent) {
await new Promise(resolve => this.queue.push(resolve));
}
this.current++;
try {
return await fn();
} finally {
this.current--;
const resolve = this.queue.shift();
if (resolve) resolve();
}
}
}
// 使用
const pool = new RequestPool(3); // 最多3个并发
const urls = ['url1', 'url2', 'url3', 'url4', 'url5'];
const results = await Promise.all(
urls.map(url => pool.run(() => fetch(url)))
);防抖与节流
javascript
// 防抖:延迟执行,重复触发会重置延迟
function debounce(fn, delay) {
let timer = null;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
// 使用:搜索框输入
const searchInput = document.getElementById('search');
searchInput.addEventListener('input', debounce((e) => {
console.log('搜索:', e.target.value);
}, 300));
// 节流:固定时间间隔执行
function throttle(fn, interval) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= interval) {
lastTime = now;
fn.apply(this, args);
}
};
}
// 使用:滚动事件
window.addEventListener('scroll', throttle(() => {
console.log('滚动位置:', window.scrollY);
}, 100));
// requestAnimationFrame 节流(更适合动画)
function throttleRAF(fn) {
let ticking = false;
return function(...args) {
if (!ticking) {
requestAnimationFrame(() => {
fn.apply(this, args);
ticking = false;
});
ticking = true;
}
};
}单例模式
javascript
// 使用闭包实现单例
const singleton = (() => {
let instance = null;
return class {
constructor() {
if (instance) {
return instance;
}
this.data = '单例数据';
instance = this;
}
static getInstance() {
if (!instance) {
instance = new singleton();
}
return instance;
}
};
})();
// 使用
const a = new singleton();
const b = singleton.getInstance();
console.log(a === b); // true深拷贝
javascript
// 简单版(不处理函数、Symbol、循环引用)
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (obj instanceof Date) {
return new Date(obj);
}
if (obj instanceof Array) {
return obj.map(item => deepClone(item));
}
const cloned = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = deepClone(obj[key]);
}
}
return cloned;
}
// 完整版(处理循环引用)
function deepCloneComplete(obj, map = new WeakMap()) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
// 处理循环引用
if (map.has(obj)) {
return map.get(obj);
}
if (obj instanceof Date) {
return new Date(obj);
}
if (obj instanceof RegExp) {
return new RegExp(obj);
}
if (obj instanceof Map) {
const cloned = new Map();
map.set(obj, cloned);
obj.forEach((value, key) => {
cloned.set(key, deepCloneComplete(value, map));
});
return cloned;
}
if (obj instanceof Set) {
const cloned = new Set();
map.set(obj, cloned);
obj.forEach(value => {
cloned.add(deepCloneComplete(value, map));
});
return cloned;
}
if (Array.isArray(obj)) {
const cloned = [];
map.set(obj, cloned);
for (let i = 0; i < obj.length; i++) {
cloned[i] = deepCloneComplete(obj[i], map);
}
return cloned;
}
const cloned = {};
map.set(obj, cloned);
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = deepCloneComplete(obj[key], map);
}
}
return cloned;
}
// 使用 structuredClone(浏览器原生 API)
const original = { a: 1, b: { c: 2 } };
const cloned = structuredClone(original);总结
本文档涵盖了 JavaScript ES6+ 的核心高级特性,每个特性都配有详细的说明、代码示例和使用场景。
学习建议
- 优先掌握核心语法:解构、箭头函数、模板字符串、扩展运算符
- 深入理解异步编程:Promise、async/await、事件循环
- 熟悉常用方法:数组方法、对象方法、字符串方法
- 关注新特性:可选链、空值合并、私有属性等 ES2020+ 特性
- 多写代码:理论结合实践,通过项目巩固所学知识