JS 进阶:ES6+ 核心语法与异步编程

在学习完 JavaScript 的基础语法(如变量、条件判断、DOM 操作)后,我们往往会直接进入前端框架(如 Vue、React)或网络请求(如 Fetch)的学习。

但在这个过程中,很多初学者会遇到一个巨大的语法断层。现代前端代码中充斥着类似 const { data } = res、...obj、() => {}、async/await 这样的写法,这些其实都是 ES6(ECMAScript 2015)及以后版本 引入的新特性。

这篇文章旨在帮你夯实这层“隐形地基”,彻底扫清阅读现代 JS 源码时的语法障碍。

1. 箭头函数:更简洁的函数写法

传统 JavaScript 中定义函数使用 function 关键字:

function add(a, b) {
  return a + b;
}

ES6 引入了箭头函数 (=>),让代码更加紧凑:

// 基础写法
const add = (a, b) => {
  return a + b;
};

// 如果函数体只有一行 return 语句,可以省略大括号和 return 关键字
const multiply = (a, b) => a * b;

// 如果只有一个参数,连小括号都可以省略
const square = x => x * x;

💡 注意: 箭头函数除了写法简洁,还有一个非常重要的特性:它没有自己的 this,它的 this 永远指向它定义时所在的上下文环境。这在很多回调函数中能解决 this 指向丢失的问题。

2. 解构赋值:优雅地提取数据

解构赋值允许我们按照一定的模式,从数组或对象中提取值,直接赋值给变量。这是在 Vue 组件和处理接口数据时最常用的语法。

2.1 对象的解构

以前我们从对象中取值需要这样写:

const user = { name: 'Alice', age: 20, city: 'Beijing' };
const name = user.name;
const age = user.age;

现在可以这样写(变量名必须和对象的属性名一致):

const user = { name: 'Alice', age: 20, city: 'Beijing' };

// 一行代码提取 name 和 age
const { name, age } = user;
console.log(name); // "Alice"

2.2 数组的解构

数组解构是按顺序提取的:

const numbers = [10, 20, 30];
// 按位置提取数组中的值
const [first, second] = numbers;

console.log(first); // 10
console.log(second); // 20

3. 展开运算符(Spread Operator)

展开运算符是三个点 ...。它可以在函数调用/数组构造时,将数组表达式或者 string 在语法层面展开;还可以在构造字面量对象时,将对象表达式按 key-value 的方式展开。

3.1 数组的展开与合并

const arr1 = [1, 2];
const arr2 = [3, 4];

// 合并数组,以前用 arr1.concat(arr2)
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4]

// 复制数组(浅拷贝)
const copy = [...arr1];

3.2 对象的展开与合并

在 Vuex 或 Pinia 的状态管理,或者传递 API 参数时非常常见:

const user = { name: 'Bob', age: 25 };

// 复制对象,并添加/覆盖新属性
const updatedUser = { 
  ...user, 
  age: 26, 
  job: 'Developer' 
};

console.log(updatedUser); 
// { name: 'Bob', age: 26, job: 'Developer' }

4. 异步编程:彻底理解 Promise

JavaScript 是单线程执行的。如果某段代码需要等待很长时间(例如网络请求、读取文件),它就会阻塞后续代码的运行,导致页面卡死。为了解决这个问题,JS 采用了异步编程机制。

过去处理异步,主要靠回调函数(Callback),但这容易导致层层嵌套的“回调地狱(Callback Hell)”。于是,ES6 引入了 Promise。

4.1 什么是 Promise?

Promise 就像是一张“承诺凭证”。想象你去奶茶店点单,店员给你一个小票(Promise 对象)。这时候奶茶还没做好(处于 pending 状态),你可以去干别的事。

  • 当奶茶做好了,店员叫你的号,这就叫 兑现(fulfilled/resolved)。
  • 如果机器坏了做不了,店员告诉你退款,这就叫 拒绝(rejected)。

4.2 基本用法:.then() 和 .catch()

// 假设 fetchData 返回一个 Promise
fetchData()
  .then(data => {
    // 当 Promise 成功兑现时,执行这里
    console.log("获取成功", data);
  })
  .catch(error => {
    // 当 Promise 被拒绝时,执行这里
    console.log("出错了", error);
  })
  .finally(() => {
    // 无论成功还是失败,最后都会执行这里(比如用来隐藏 loading 动画)
    console.log("请求结束");
  });

5. 异步终极方案:async / await

虽然 Promise 解决了回调地狱,但用一堆 .then() 链式调用,代码看着还是不够像“正常”的同步代码。

于是 ES8 (ECMAScript 2017) 推出了 async 和 await,它们是基于 Promise 的语法糖,能让我们用写同步代码的方式来写异步逻辑。

5.1 核心规则

  1. async 用于声明一个异步函数,该函数会隐式地返回一个 Promise。
  2. await 只能在 async 函数内部使用。它会暂停代码在这个位置的执行,直到后面的 Promise 完成并返回结果,然后再往下走。

5.2 代码对比

用 .then() 写法(Fetch 请求):

function getUser() {
  // fetch 本身返回一个 Promise
  fetch('https://api.example.com/user')
    // 第一步:把响应体解析成 JSON
    .then(res => res.json())
    // 第二步:拿到真正的数据对象
    .then(data => {
      console.log(data);
    });
}

用 async / await 写法:

async function getUser() {
  try {
    // 代码会在这里等待 fetch 完成
    const res = await fetch('https://api.example.com/user');
    // 代码会在这里等待 json 解析完成
    const data = await res.json();
    console.log(data);
  } catch (error) {
    // 使用 try...catch 来捕获异常
    console.error("请求失败", error);
  }
}

可以明显看到,使用 async/await 后,没有了回调函数的嵌套,代码阅读起来是从上往下直线的,非常清晰。

掌握了箭头函数、解构赋值、展开运算符以及 async/await,你就彻底拿到了阅读现代前端代码的“通行证”。接下来再去学习 Vue 3 或网络请求,将会如履平地。