ES6回顾:闭包->(优点:实现工厂函数、记忆化和异步实现)、(应用场景:Promise的then与catch的回调、async/await、柯里化函数)

闭包讲解

ES6回顾:闭包->(优点:实现工厂函数、记忆化和异步实现)、(应用场景:Promise的then与catch的回调、async/await、柯里化函数)

以下是与 JavaScript 闭包相关的常见考点整理,结合 Promise、async/await、缓存结果、工厂函数、柯里化等内容,并依据我搜索到的资料进行详细说明:


1. 闭包(Closure)

  • 定义:由函数及其引用的外部词法环境变量组成,即使外部函数执行完毕,内部函数仍能访问这些变量 。
  • 作用
    • 延长外部函数变量的生命周期,使外部可操作内部数据(如模块化封装)。
    • 避免全局变量污染,实现私有变量 。
  • 缺点:不当使用会导致内存泄漏(变量无法被回收)。
  • 应用场景
    • 缓存结果:通过闭包保存计算结果,避免重复计算(记忆化函数)。
    • 工厂函数:生成带有特定配置的函数实例(如计数器生成器)。
    • 柯里化:拆分多参数函数为链式单参数调用,依赖闭包保存中间参数 。
    • 异步回调:在事件处理或定时器中保留上下文 。

2. Promise

  • 核心概念
    • 三种状态:pendingfulfilledrejected,状态一旦改变不可逆 。
    • 解决回调地狱(Callback Hell),支持链式调用 .then().catch()
  • 常用方法
    • Promise.all():所有 Promise 成功时返回结果数组,任一失败立即拒绝 。
    • Promise.race():首个完成的 Promise 决定最终状态 。
    • Promise.resolve()/Promise.reject():快速创建成功/失败的 Promise 。
  • 手写实现 :常考手写 Promise.allPromise.race 的实现 。

3. async/await

  • 本质:基于 Generator 和 Promise 的语法糖,使异步代码更接近同步写法 。
  • 规则
    • async 函数返回 Promise 对象,return 值会被 Promise.resolve() 包装 。
    • await 后接 Promise,暂停当前函数执行直到 Promise 完成(属于微任务)。
    • await 只能在 async 函数内使用,否则需通过立即调用异步函数(如 (async () => { ... })())。
  • 错误处理 :用 try...catch 捕获 await 后的 Promise 拒绝 。
  • 执行顺序await 后的代码相当于放入 .then() 中,属于微任务队列 。
javascript 复制代码
function asyncToGenerator(fn) {
    // 返回函数
    return function() {
        const gen = fn.apply(this, arguments);//生成器
        // 方法返回一个Promise对象
        return new Promise((resolve, reject) => {
            function step(key, arg) {
                // console.log(arg)
                let result;
                try {
                    result = gen[key](arg);
                    //生成器返回数据{value,done:bool}
                    // 移除调试日志以保持输出整洁
                } catch (error) {
                    return reject(error);
                }
                //如果执行器执行完成
                if (result.done) {
                    return resolve(result.value);
                }
                //如果执行器未完成,将当前结果传入Promise的resolve方法中,并递归调用step方法
                Promise.resolve(result.value).then(
                    v => step("next", v),
                    e => step("throw", e)
                );
            }
            step("next");
        });
    };
}

// 使用示例(已修复)
const asyncFunction = asyncToGenerator(function* () {
    // 修正 Promise 的创建方式,使用箭头函数包裹 resolve
    const result = yield new Promise(resolve => setTimeout(() => resolve("result"), 1000));
    const result1 = yield new Promise(resolve => setTimeout(() => resolve("result2"), 2000));
    console.log(result, result1); // 输出: "result" "result2"(3秒后)
});

asyncFunction(); // 启动执行

4. 柯里化(Currying)

  • 定义:将多参数函数转换为一系列单参数函数链式调用的技术,依赖闭包保存中间参数 。
  • 示例
javascript 复制代码
  function add(x) {
    return function(y) {
      return x + y;
    };
  }
  add(2)(3); // 5
  • 应用:参数复用、延迟执行、函数组合 。

5. 工厂函数

  • 作用:通过闭包生成具有独立状态的函数实例。例如生成独立的计数器:
javascript 复制代码
  function createCounter() {
    let count = 0;
    return function() {
      return ++count;
    };
  }
  const counter1 = createCounter(); // 独立作用域
  • 场景:封装私有变量、实现模块化 。

6. 闭包与异步编程的结合

  • 事件回调 :在闭包中使用 async/await 处理异步逻辑,避免全局变量污染 。
javascript 复制代码
  button.addEventListener('click', () => {
    (async () => {
      const data = await fetchData();
      console.log(data);
    })();
  });
  • 定时器 :闭包保存定时器状态,结合 async/await 控制执行流程 。

7. 内存管理

  • 内存泄漏 :闭包长期引用外部变量会导致内存无法释放,需及时解除引用(如手动置 null)。
  • 优化:避免不必要的闭包,或在不需要时清除事件监听器、定时器等 。

总结

闭包是 JavaScript 的核心概念,与异步编程(Promise/async/await)、函数式编程(柯里化、工厂函数)紧密相关。理解闭包的作用域机制、内存管理,以及与其他特性的结合方式,是应对面试和实际开发的关键。


async/await 底层实现解析

async/await 是 JavaScript 中处理异步操作的语法糖,其底层实现基于 PromiseGenerator(生成器) 的协同机制。通过分析资料中的代码转换、设计原理和规范定义,其核心实现逻辑可拆解如下:


一、核心依赖:Promise 与 Generator 的协作
  1. Promise 的基础作用

    async/await 的异步控制完全依赖于 Promise 的链式调用。每个 await 表达式本质上会创建一个 Promise,并将后续代码包装到 .then() 中等待执行。例如:

    javascript 复制代码
    async function foo() {
      const result = await somePromise; // 等价于 somePromise.then(result => {...})
    }

    这种设计使得异步操作的 状态管理错误传播 能够通过 Promise 链式结构实现。

  2. Generator 的流程控制

    Generator 函数通过 yield 关键字暂停执行,并通过迭代器(Iterator)手动恢复。async/await 利用这一特性,将异步代码的 暂停-恢复机制 转化为生成器函数的 yield 操作。例如,以下代码:

    javascript 复制代码
    async function a() {
      const res = await asyncTask();
    }

    会被 Babel 转换为使用 Generator 的代码:

    javascript 复制代码
    function a() {
      return __awaiter(this, void 0, void 0, function* () {
        const res = yield asyncTask();
      });
    }

    这里的 yield 替代了 await,而 __awaiter 函数负责管理生成器的迭代。


二、转换逻辑:代码降级与执行器封装

通过 Babel/TypeScript 等工具的代码转换,async/await 的实现可拆解为以下步骤:

  1. 生成器函数包装

    async 函数被转换为生成器函数,await 被替换为 yield。例如:

    javascript 复制代码
    // 原始代码
    async function fetchData() {
      const data = await fetch(url);
      return data;
    }
    
    // 转换后代码(简化)
    function fetchData() {
      return __awaiter(this, function* () {
        const data = yield fetch(url);
        return data;
      });
    }
  2. 执行器函数(如 __awaiter)的作用

    执行器负责驱动生成器的迭代,并处理 Promise 的链式调用。其核心逻辑如下:

    • 将生成器的每个 yield 值包装为 Promise。
    • 通过 generator.next(value) 将 Promise 的结果传递回生成器。
    • 捕获错误并通过 generator.throw(error) 抛出异常。
    javascript 复制代码
    function __awaiter(generator) {
      return new Promise((resolve, reject) => {
        function step(result) {
          if (result.done) {
            resolve(result.value);
          } else {
            Promise.resolve(result.value).then(
              value => step(generator.next(value)), // 传递结果并继续迭代
              error => step(generator.throw(error)) // 抛出错误
            );
          }
        }
        step(generator.next());
      });
    }

    此过程实现了 自动迭代错误冒泡,使代码看似同步执行。


三、执行顺序与事件循环的关联
  1. 微任务队列的调度
    await 后的代码会被包装为微任务(Microtask),在 Promise 解决后加入微任务队列。例如:

    javascript 复制代码
    async function demo() {
      console.log(1);
      await Promise.resolve();
      console.log(2); // 相当于 Promise.resolve().then(() => console.log(2))
    }
    demo();
    console.log(3);
    // 输出顺序:1 → 3 → 2

    这种机制确保了异步代码的执行不会阻塞主线程。

  2. 协程(Coroutine)模型的实现

    async/await 通过生成器和 Promise 模拟了协程的 挂起-恢复 行为:

    • 挂起 :在 await 处暂停生成器,释放主线程。
    • 恢复:当 Promise 解决后,通过执行器继续生成器的迭代。

四、错误处理机制的实现
  1. try/catch 的转换

    async 函数中的 try/catch 会被转换为 Promise 的 .catch() 链。例如:

    javascript 复制代码
    async function foo() {
      try {
        await somePromise();
      } catch (err) {
        handleError(err);
      }
    }

    转换后逻辑:

    javascript 复制代码
    function* foo() {
      try {
        const result = yield somePromise();
      } catch (err) {
        handleError(err);
      }
    }

    执行器在生成器抛出错误时触发 reject

  2. 未捕获异常的传播

    若未使用 try/catch,错误会通过 Promise 链冒泡到顶层,触发 unhandledrejection 事件。


五、性能与优化考量
  1. 生成器与 Promise 的开销

    async/await 相比原生 Promise 链会引入额外开销(如生成器对象的创建),但在现代引擎中差异可忽略。

  2. 并发的实现方式

    需显式使用 Promise.all() 实现并行,避免顺序等待:

    javascript 复制代码
    async function parallel() {
      const [a, b] = await Promise.all([task1(), task2()]); // 并行执行
    }

    若直接顺序 await,会导致任务串行执行。


总结:async/await 的架构设计

层级 实现机制 作用
语法层 async/await 关键字 提供同步代码风格的异步写法
转换层 Babel/TypeScript 代码降级 将 async/await 转为 Generator + Promise
运行时层 生成器迭代器 + Promise 链 管理暂停/恢复、错误传播
事件循环层 微任务队列调度 确保异步代码非阻塞执行

通过多层抽象,async/await 将复杂的异步流程控制简化为直观的同步式代码,同时保持与 Promise 的完全兼容性。

相关推荐
returnShitBoy1 小时前
前端面试:如何实现预览 PDF 文件?
前端·pdf
烂蜻蜓2 小时前
HTML 表格的详细介绍与应用
开发语言·前端·css·html·html5
returnShitBoy4 小时前
前端面试:axios 是否可以取消请求?
前端
u0103754564 小时前
fiddler+雷电模拟器(安卓9)+https配置
前端·测试工具·fiddler
海上彼尚4 小时前
Vue3中全局使用Sass变量方法
前端·css·sass
ᥬ 小月亮4 小时前
TypeScript基础
前端·javascript·typescript
鱼樱前端4 小时前
Vue3+TS 视频播放器组件封装(Video.js + Hls.js 最佳组合)
前端·javascript·vue.js
烛阴5 小时前
JavaScript 函数进阶之:Rest 参数与 Spread 语法(二)
前端·javascript
天天向上10245 小时前
vue项目清理node_modules缓存
前端·vue.js·缓存