前端笔记-JavaScript部分(中)

JavaScript学习目标

|------------|---------------------|
| JavaScript | 1、变量、数据类型与运算符 |
| JavaScript | 2、函数与作用域 |
| JavaScript | 3、对象与原型链 |
| JavaScript | 4、ES6语法概览 |
| JavaScript | 5、学习了解DOM以及相关的DOM操作 |
| JavaScript | 6、js事件循环 |
| JavaScript | 7、Promise |
| JavaScript | 8、async/await |
| JavaScript | 9、解构赋值与扩展运算符 |
| JavaScript | 10、箭头函数与this绑定 |
| JavaScript | 11、xhr基础(基本了解即可) |

1-3 前端笔记-JavaScript部分(上)-CSDN博客

4 前端笔记-ECMAScript语法概览-CSDN博客

6 前端笔记-JS的事件循环机制-CSDN博客

1. Promise

在Promise开始之前,需要了解一下JavaScript的异步编程 JavaScript 异步编程 | 菜鸟教程

Promise是ES6引入的异步编程解决方案,它代表一个尚未完成但预期将来会完成的操作。

有着三种状态:

状态 描述 触发方式
Pending 初始状态,既不是成功也不是失败 创建Promise时默认状态
Fulfilled 操作成功完成 resolve()被调用
Rejected 操作失败 reject()被调用
Settled 已定型(Fulfilled或Rejected) 最终状态

1.1Promise构造器:创建一个"承诺"

想象Promise就像你点外卖时商家给你的取餐号小票:

复制代码
// 创建一个新的Promise(就像点餐拿到小票)
const mealPromise = new Promise((resolve, reject) => {
  // 厨房开始做饭(异步操作)
  setTimeout(() => {
    const isSuccess = Math.random() > 0.3; // 70%成功率
    
    if(isSuccess) {
      resolve("宫保鸡丁套餐"); // 做饭成功
    } else {
      reject("抱歉,鸡肉用完了"); // 做饭失败
    }
  }, 2000); // 需要2分钟做饭时间
});

new Promise:就像点餐拿到取餐号小票;resolve:厨师做好饭后叫号;reject:厨师告诉你材料不够做不了

1.2 Promise函数:处理"承诺"的结果

拿到小票后,你要决定怎么处理这个承诺:

复制代码
mealPromise
  .then(food => {
    console.log(`我吃到了: ${food}`); // 成功时执行
  })
  .catch(error => {
    console.log(`点餐失败: ${error}`); // 失败时执行
  })
  .finally(() => {
    console.log('用餐流程结束'); // 无论成功失败都会执行
  });

.then():饭菜做好了你要做什么(成功回调)

.catch():如果做不了你要怎么处理(失败回调)

.finally():不管成功失败,最后都要收拾餐桌

1.3 Promise 静态方法

通过上述俩个通俗的例子之后,我们已经能接触到其中的一些静态方法,还有一些静态方法如下:

方法 用途 示例
Promise.resolve() 创建立即成功的 Promise Promise.resolve('直接成功')
Promise.reject() 创建立即失败的 Promise Promise.reject('直接失败')
Promise.all() 等待全部成功或任意一个失败 Promise.all([p1, p2, p3])
Promise.race() 采用第一个 settled 的结果 Promise.race([p1, p2])
Promise.allSettled() 等待所有 Promise 完成

需要注意的是

  • resolve 和 reject 的作用域只有起始函数,不包括 then 以及其他序列;
  • resolve 和 reject 并不能够使起始函数停止运行,别忘了 return。

我们在什么时候适合使用Promise而不是传统的回调函数呢?

1.4 何时使用 Promise

1.4.1 多层嵌套异步操作(回调地狱)
复制代码
getData(function(a) {
  getMoreData(a, function(b) {
    getMoreData(b, function(c) {
      // 更多嵌套...
    });
  });
});

Promise 解决方案​:

复制代码
getData()
  .then(a => getMoreData(a))
  .then(b => getMoreData(b))
  .then(c => { /* 清晰逻辑 */ });
1.4.2. 需要组合多个异步操作时

典型场景​:

  • 并行执行多个独立请求
  • 顺序执行有依赖关系的请求
  • 竞速执行(取最快结果)

Promise 方法​:

复制代码
// 并行执行
Promise.all([fetchUser(), fetchPosts()])
  .then(([user, posts]) => {});

// 顺序执行
fetchUser()
  .then(user => fetchPosts(user.id))
  .then(posts => {});

// 竞速执行
Promise.race([fetchFastAPI(), fetchSlowAPI()])
  .then(firstResult => {});

2. 异步函数(async/await):像同步代码一样写异步

async/await是建立在Promise之上的语法糖,让异步代码看起来像同步代码一样易读

关键字 作用 示例
async 声明一个函数是异步的,该函数总是返回 Promise async function fetchData()
await 暂停异步函数的执行,等待 Promise 完成,然后继续执行 const data = await fetch()

2.1 声明async函数

复制代码
// 函数声明式
async function getUser() {
  return { name: '小明', age: 18 };
}

// 函数表达式
const getData = async function() { /*...*/ };

// 箭头函数
const fetchData = async () => { /*...*/ };

// 对象方法
const api = {
  async getPosts() { /*...*/ }
};

2.2 await使用规则

复制代码
async function loadContent() {
  // 正确:等待一个 Promise
  const user = await getUser();
  
  // 错误:不能用在普通函数中
  function normalFunc() {
    await doSomething(); // SyntaxError
  }
  
  // 正确:在 IIFE 中可以使用
  (async () => {
    await doSomething();
  })();
}

async/await可以用更自然的方式处理异步操作:

复制代码
// 声明一个异步函数(标记为async)
async function orderMeal() {
  try {
    console.log('开始点餐...');
    
    // await就像在说"等饭菜做好再继续"
    const food = await mealPromise; 
    
    console.log(`我吃到了: ${food}`);
    console.log('开始享用美食!');
  } catch (error) {
    console.log(`点餐失败: ${error}`);
    console.log('只能吃泡面了...');
  } finally {
    console.log('用餐时间结束');
  }
}

// 调用异步函数
orderMeal();
  • async function:声明一个会涉及等待的操作流程
  • await:就像在餐厅坐着等,不干别的,直到饭菜做好
  • try/catch:成功和失败的不同处理方式

2.3 完整示例

复制代码
// 1. 创建洗衣机Promise
const washClothes = new Promise((resolve) => {
  console.log('把脏衣服放进洗衣机');
  setTimeout(() => resolve('干净衣服'), 3000); // 洗30分钟
});

// 2. 使用Promise的then方式
washClothes
  .then(cleanClothes => {
    console.log(`拿到${cleanClothes},开始晾衣服`);
  });

// 3. 使用async/await方式
async function doLaundry() {
  console.log('早上开始洗衣服');
  const result = await washClothes; // 等待洗衣机完成
  console.log(`下午拿到${result},开始晾衣服`);
  console.log('晾衣完成');
}
概念 生活比喻 代码示例 特点
Promise构造器 家电使用说明书 new Promise() 创建异步任务
resolve/reject 家电完成/故障提示音 resolve()/reject() 表示任务成功/失败
.then() 家电完成后要做的事情 .then(result => {...}) 成功回调
.catch() 家电故障后的应急处理 .catch(error => {...}) 失败回调
async函数 包含等待的家务流程 async function doTask() 声明异步流程
await 等待家电完成的时间 await somePromise 暂停执行直到Promise完成

记住这个简单的规律:

  • 需要等待的操作 → 用Promise包装
  • 需要处理Promise结果 → 用.then/.catch或async/await
  • 想让代码更易读 → 优先用async/await

2.4 Promise与async/await对比

特性 Promise async/await
语法结构 链式调用(.then().catch()) 类似同步代码的写法
错误处理 使用.catch()或then的第二个参数 使用try/catch块
可读性 链式调用可能造成嵌套 线性结构,更易阅读
调试 较难调试,断点可能不直观 更容易调试,像同步代码一样
返回值 总是返回Promise 总是返回Promise
并行处理 使用Promise.all 结合Promise.all使用

2.5 常见错误与解决方案

错误类型 示例 解决方案
忘记await asyncFunc() // 没有await 确保调用async函数时使用await或.then
未处理的Promise拒绝 没有.catch()的Promise链 总是添加错误处理
过度顺序化 不必要的顺序await 使用Promise.all进行并行处理
async函数中的forEach问题 在forEach中使用await无效 改用for...of循环
忽略返回值 不处理async函数返回的Promise 处理返回值或明确忽略

3 解构赋值与扩展运算符

3.1解构赋值:拆箱取物的艺术

3.3.1. 数组解构:像拆快递一样拿数据
复制代码
// 想象这是一个快递包裹
const package = ['手机', '充电器', '耳机']

// 拆包取出内容
const [phone, charger, earphone] = package

console.log(phone)     // '手机'
console.log(charger)   // '充电器'
console.log(earphone) // '耳机'

跳过某些项​(像跳过不感兴趣的赠品):

复制代码
const [mainProduct, , freeGift] = ['电视', '支架', '清洁布']
console.log(mainProduct) // '电视'
console.log(freeGift)    // '清洁布'

默认值​(防止拿到undefined):

复制代码
const [main = '默认商品', accessory = '无赠品'] = ['笔记本电脑']
console.log(main)      // '笔记本电脑'
console.log(accessory) // '无赠品'
3.3.2. 对象解构:像填表格一样提取属性
复制代码
// 学生信息表
const student = {
  name: '小明',
  age: 18,
  grade: '高三'
}

// 提取需要的信息
const { name, age } = student

console.log(name) // '小明'
console.log(age)  // 18

重命名​(像给数据起别名):

复制代码
const { name: studentName, grade: classLevel } = student
console.log(studentName) // '小明'
console.log(classLevel)  // '高三'

嵌套解构​(拆多层包装):

复制代码
const company = {
  name: 'TechCo',
  departments: {
    dev: { count: 20 },
    hr: { count: 5 }
  }
}

const {
  name: companyName,
  departments: { dev: { count: devCount } }
} = company

console.log(companyName) // 'TechCo'
console.log(devCount)    // 20

3.2 扩展运算符:复制粘贴的魔法

3.2.1. 数组扩展:像复印机一样工作

复制数组​(真正的拷贝):

复制代码
const origin = [1, 2, 3]
const copy = [...origin] // 相当于 [1, 2, 3]

origin.push(4)
console.log(origin) // [1, 2, 3, 4]
console.log(copy)    // [1, 2, 3] (不受影响)

合并数组​(像调鸡尾酒):

复制代码
const fruits = ['苹果', '香蕉']
const veggies = ['胡萝卜', '菠菜']

const shoppingList = [...fruits, ...veggies, '鸡蛋']
// ['苹果', '香蕉', '胡萝卜', '菠菜', '鸡蛋']

函数参数​(像把一盒糖倒出来):

复制代码
function orderDesserts(main, ...others) {
  console.log(`主甜点: ${main}`)
  console.log(`其他甜点: ${others.join(', ')}`)
}

orderDesserts('蛋糕', '布丁', '冰淇淋', '马卡龙')
// 主甜点: 蛋糕
// 其他甜点: 布丁, 冰淇淋, 马卡龙
3.3.2. 对象扩展:像盖章一样复制属性

复制对象​:

复制代码
const user = { name: 'Lucy', age: 25 }
const userCopy = { ...user }

user.age = 26
console.log(user)      // {name: "Lucy", age: 26}
console.log(userCopy)  // {name: "Lucy", age: 25}

合并对象​(像混搭服装):

复制代码
const baseInfo = { name: 'Mike', gender: 'male' }
const workInfo = { job: 'developer', level: 3 }

const employee = { ...baseInfo, ...workInfo, salary: 20000 }
// {name: "Mike", gender: "male", job: "developer", level: 3, salary: 20000}

属性覆盖​(后者覆盖前者):

复制代码
const defaultSettings = { theme: 'light', fontSize: 16 }
const userSettings = { theme: 'dark' }

const finalSettings = { ...defaultSettings, ...userSettings }
// {theme: "dark", fontSize: 16}

3.3 对比总结表

特性 解构赋值 扩展运算符
主要用途 从数据结构中提取值 组合/复制数据结构
工作对象 数组和对象 数组和对象
是否改变原数据
常见场景 函数参数提取、模块导入 数组合并、对象浅拷贝、函数传参
默认值支持 支持 不支持
重命名支持 支持(对象) 不支持
嵌套结构处理 支持 浅拷贝

3.4 实用技巧与注意事项

3.4.1. 解构赋值的妙用

交换变量​(不用临时变量):

复制代码
let a = 1, b = 2;
[a, b] = [b, a] // 交换
console.log(a, b) // 2, 1

函数默认参数​:

复制代码
function connect({ host = 'localhost', port = 8080 } = {}) {
  console.log(`连接到 ${host}:${port}`)
}

connect() // 连接到 localhost:8080
connect({ port: 3000 }) // 连接到 localhost:3000
3.4.2. 扩展运算符的陷阱

浅拷贝问题​:

复制代码
const original = { a: 1, b: { c: 2 } }
const copy = { ...original }

original.b.c = 3
console.log(copy.b.c) // 3 (也被修改了)

只适用于可迭代对象​:

复制代码
// 错误用法
const obj = { 0: 'a', 1: 'b', length: 2 }
[...obj] // TypeError: obj is not iterable

// 正确用法(类数组对象)
const arrayLike = { 0: 'a', 1: 'b', length: 2 }
Array.from(arrayLike) // ['a', 'b']

记住这两个特性:

  • 从复杂数据中精准提取所需(解构)
  • 轻松组合/复制数据而不改变原数据(扩展)
  • 写出更简洁易读的代码
相关推荐
恋猫de小郭30 分钟前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
wdfk_prog8 小时前
[Linux]学习笔记系列 -- [drivers][input]input
linux·笔记·学习
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
ouliten8 小时前
cuda编程笔记(36)-- 应用Tensor Core加速矩阵乘法
笔记·cuda
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端