前端笔记-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']

记住这两个特性:

  • 从复杂数据中精准提取所需(解构)
  • 轻松组合/复制数据而不改变原数据(扩展)
  • 写出更简洁易读的代码
相关推荐
KoiHeng2 小时前
操作系统简要知识
linux·笔记
巴伦是只猫3 小时前
【机器学习笔记Ⅰ】11 多项式回归
笔记·机器学习·回归
zwjapple6 小时前
docker-compose一键部署全栈项目。springboot后端,react前端
前端·spring boot·docker
DKPT6 小时前
Java桥接模式实现方式与测试方法
java·笔记·学习·设计模式·桥接模式
像风一样自由20208 小时前
HTML与JavaScript:构建动态交互式Web页面的基石
前端·javascript·html
巴伦是只猫8 小时前
【机器学习笔记Ⅰ】13 正则化代价函数
人工智能·笔记·机器学习
aiprtem8 小时前
基于Flutter的web登录设计
前端·flutter
浪裡遊9 小时前
React Hooks全面解析:从基础到高级的实用指南
开发语言·前端·javascript·react.js·node.js·ecmascript·php
why技术9 小时前
Stack Overflow,轰然倒下!
前端·人工智能·后端
GISer_Jing9 小时前
0704-0706上海,又聚上了
前端·新浪微博