跳转到内容

JavaScript 高级用法(ES6+)

本文档涵盖了 JavaScript ES6+ 的高级特性,每个特性都包含详细说明、代码示例和使用场景。

目录

  1. 核心语法
  2. 对象与数组高级用法
  3. 异步编程
  4. 集合与数据结构
  5. 函数进阶
  6. 类与面向对象
  7. ES2020+ 新特性
  8. 事件循环与执行机制
  9. 实用技巧

核心语法

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)

说明

更简洁的函数语法,且不绑定自己的 thisargumentssupernew.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');
// 2

Array.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]; // 50

Array.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)??

说明

当左侧是 nullundefined 时返回右侧值,否则返回左侧值。

|| 的区别

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_; // SyntaxError

String.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'); // false

Array.prototype.at() / toReversed() / toSorted() / with()

已在 数组高级方法 章节详细说明。


事件循环与执行机制

宏任务与微任务

说明

JavaScript 是单线程的,通过事件循环处理异步任务。

任务分类

类型说明举例
同步任务立即执行函数调用、表达式计算
宏任务(Macro Task)事件循环队列setTimeoutsetInterval、I/O、UI渲染
微任务(Micro Task)当前任务后立即执行Promise.thenqueueMicrotaskMutationObserver

执行顺序

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+ 的核心高级特性,每个特性都配有详细的说明、代码示例和使用场景。

学习建议

  1. 优先掌握核心语法:解构、箭头函数、模板字符串、扩展运算符
  2. 深入理解异步编程:Promise、async/await、事件循环
  3. 熟悉常用方法:数组方法、对象方法、字符串方法
  4. 关注新特性:可选链、空值合并、私有属性等 ES2020+ 特性
  5. 多写代码:理论结合实践,通过项目巩固所学知识

参考资源

Will Try My Best.