<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>技术笔记 on 日日是好日</title><link>https://jjjjjjy.github.io/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/</link><description>Recent content in 技术笔记 on 日日是好日</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Thu, 19 Mar 2026 16:15:12 +0800</lastBuildDate><atom:link href="https://jjjjjjy.github.io/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/index.xml" rel="self" type="application/rss+xml"/><item><title>数组拍平</title><link>https://jjjjjjy.github.io/posts/leetcode/flatten/</link><pubDate>Thu, 19 Mar 2026 16:15:12 +0800</pubDate><guid>https://jjjjjjy.github.io/posts/leetcode/flatten/</guid><description>&lt;h2 id="背景"&gt;背景
&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;为什么要写这篇文章？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="核心内容"&gt;核心内容
&lt;/h2&gt;&lt;h3 id="1-概念说明"&gt;1️⃣ 概念说明
&lt;/h3&gt;&lt;h3 id="2-使用方式"&gt;2️⃣ 使用方式
&lt;/h3&gt;&lt;h3 id="3-实际案例"&gt;3️⃣ 实际案例
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// 示例代码
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;Hello Vite&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>SSE</title><link>https://jjjjjjy.github.io/posts/browser/communication/sse/</link><pubDate>Fri, 13 Mar 2026 16:39:13 +0800</pubDate><guid>https://jjjjjjy.github.io/posts/browser/communication/sse/</guid><description>&lt;h4 id=""&gt;
&lt;/h4&gt;&lt;p&gt;SSE（Server-Sent Events）是一种基于 HTTP 的服务器推送技术，浏览器通过 EventSource 建立长连接，服务器可以持续向客户端发送 text/event-stream 数据流。它是单向通信，适合实时推送场景，例如 AI 流式输出、通知系统、日志流等。&lt;/p&gt;
&lt;p&gt;特点：&lt;/p&gt;
&lt;p&gt;基于 HTTP&lt;/p&gt;
&lt;p&gt;单向通信（Server → Client）&lt;/p&gt;
&lt;p&gt;浏览器通过 EventSource API 接收服务器不断推送的数据&lt;/p&gt;
&lt;p&gt;连接建立后 服务器可以持续发送数据流&lt;/p&gt;
&lt;p&gt;优点:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;简单易用：SSE提供了一种在服务器和客户端之间建立单向连接的直接方法。客户端订阅了SSE端点，然后服务器可以通过此连接将数据推送到客户端，而不需要客户端不断发送请求&lt;/li&gt;
&lt;li&gt;减少网络开销：与持续轮询技术（即每隔几秒钟让每个客户端从服务器请求数据）相比，SSE显著减少了网络开销。&lt;/li&gt;
&lt;li&gt;标准化协议：SSE基于HTTP协议，使其易于部署并与现有的web基础设施兼容。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="为什么sse适合ai流式输出"&gt;为什么SSE适合AI流式输出
&lt;/h4&gt;&lt;p&gt;因为服务器可以持续push数据&lt;/p&gt;
&lt;h4 id="sse-为什么比-websocket-简单"&gt;SSE 为什么比 WebSocket 简单
&lt;/h4&gt;&lt;p&gt;因为SSE基于 HTTP，不需要协议升级&lt;/p&gt;</description></item><item><title>Promise</title><link>https://jjjjjjy.github.io/posts/javascript/promise/</link><pubDate>Fri, 13 Mar 2026 14:47:52 +0800</pubDate><guid>https://jjjjjjy.github.io/posts/javascript/promise/</guid><description>&lt;h4 id="promise-定义"&gt;Promise 定义
&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;Promise 是 JS 提供的&lt;strong&gt;异步&lt;/strong&gt;解决方案。&lt;/li&gt;
&lt;li&gt;内部存在三种状态：pending fulfilled rejected。状态一旦改变不可逆。&lt;/li&gt;
&lt;li&gt;Promise 内部维护两个回调队列：onFulfilledCallbacks和onRejectedCallbacks。then 会把回调存入队列。当 resolve 或 reject 时，会执行对应回调。&lt;/li&gt;
&lt;li&gt;then 返回新的 Promise，因此可以实现链式调用。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="promise-状态"&gt;Promise 状态
&lt;/h4&gt;&lt;p&gt;pending 等待
fulfilled 成功
rejected 失败&lt;/p&gt;
&lt;p&gt;状态变化
pending → fulfilled
pending → rejected&lt;/p&gt;
&lt;p&gt;状态一旦改变就不能再变。【原因:避免竞态条件】&lt;/p&gt;
&lt;h4 id="promise-特点"&gt;Promise 特点
&lt;/h4&gt;&lt;p&gt;Promise 最大的特点是 then会返回新的Promise。也就是Promise可以进行链式调用，因此可以解决回调地狱&lt;/p&gt;
&lt;h4 id="promise-本质"&gt;Promise 本质
&lt;/h4&gt;&lt;p&gt;Promise的本质是 状态 + 回调队列&lt;/p&gt;</description></item><item><title>浏览器事件循环</title><link>https://jjjjjjy.github.io/posts/browser/event-loop/</link><pubDate>Thu, 12 Mar 2026 21:13:47 +0800</pubDate><guid>https://jjjjjjy.github.io/posts/browser/event-loop/</guid><description>&lt;h2 id="谨记"&gt;谨记
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;JavaScript 在浏览器中是单线程执行的。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;但需要注意：&lt;/p&gt;
&lt;p&gt;JavaScript 本身是单线程执行的，但浏览器运行环境是多线程的。
异步任务由浏览器的其他线程处理，完成后通过 &lt;strong&gt;事件循环（Event Loop）&lt;/strong&gt; 回调到 JS 主线程执行。
如果需要真正的并行计算，可以通过 &lt;strong&gt;Web Worker&lt;/strong&gt; 创建新的 JS 线程。&lt;/p&gt;
&lt;h4 id="浏览器事件循环是什么"&gt;浏览器事件循环是什么
&lt;/h4&gt;&lt;p&gt;JavaScript 在浏览器中是 &lt;strong&gt;单线程执行的&lt;/strong&gt;，但浏览器需要处理很多异步任务，例如：网络请求、定时器、用户交互事件、DOM 事件。&lt;/p&gt;
&lt;p&gt;为了在不阻塞主线程的情况下处理这些异步任务，浏览器引入了 &lt;strong&gt;事件循环（Event Loop）机制&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;🌟 &lt;strong&gt;核心流程：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;同步代码&lt;/strong&gt;进入调用栈 &lt;code&gt;Call Stack&lt;/code&gt; 执行&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;异步任务&lt;/strong&gt;交给浏览器的 &lt;code&gt;Web APIs&lt;/code&gt; 处理（如定时器、网络请求）&lt;/li&gt;
&lt;li&gt;异步任务完成后，其回调函数进入 &lt;strong&gt;任务队列 &lt;code&gt;Task Queue&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Event Loop&lt;/strong&gt; 持续检测调用栈是否为空
如果为空，则把任务队列中的任务放入调用栈执行&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="为什么需要事件循环"&gt;为什么需要事件循环
&lt;/h4&gt;&lt;p&gt;由于 JavaScript 是单线程的：同一时间只能执行一段代码，如果没有事件循环，setTimeout就需要等待对应的time才能继续执行，而这会导致：页面卡死。&lt;/p&gt;
&lt;p&gt;因此&lt;strong&gt;浏览器设计成 = 单线程 JS + 事件循环调度 + 浏览器异步线程&lt;/strong&gt;。从而实现：非阻塞异步&lt;/p&gt;
&lt;h4 id="浏览器运行-js-的基本结构"&gt;浏览器运行 JS 的基本结构
&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;同步代码进入 调用栈&lt;code&gt;Call Stack&lt;/code&gt; 执行&lt;/li&gt;
&lt;li&gt;异步任务交给浏览器的 Web APIs（例如定时器、网络请求）&lt;/li&gt;
&lt;li&gt;异步任务完成后，其回调函数会进入 任务队列&lt;code&gt;Task Queue&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Event Loop 会不断检查 调用栈是否为空，如果为空，就把任务队列中的任务放入调用栈执行。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="任务队列宏任务-与-微任务"&gt;任务队列：宏任务 与 微任务
&lt;/h4&gt;&lt;p&gt;任务队列中的任务分为两类：Macro Task（宏任务）和 Micro Task（微任务）&lt;/p&gt;
&lt;h4 id="宏任务macro-task"&gt;宏任务（Macro Task）
&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;定义：&lt;/strong&gt; 宏任务是由浏览器调度的任务，每次事件循环会执行 &lt;strong&gt;一个宏任务&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;常见宏任务：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;整个 JS 文件（script）&lt;/li&gt;
&lt;li&gt;setTimeout&lt;/li&gt;
&lt;li&gt;setInterval&lt;/li&gt;
&lt;li&gt;DOM 事件&lt;/li&gt;
&lt;li&gt;postMessage&lt;/li&gt;
&lt;li&gt;MessageChannel&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="微任务micro-task"&gt;微任务（Micro Task）
&lt;/h4&gt;&lt;p&gt;微任务会在 &lt;strong&gt;当前宏任务执行结束后立即执行&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;常见微任务：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Promise的回调&lt;/strong&gt;[.then、.catch、.finally]&lt;/li&gt;
&lt;li&gt;queueMicrotask&lt;/li&gt;
&lt;li&gt;MutationObserver&lt;/li&gt;
&lt;li&gt;await 后面的代码会进入微任务队列&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="浏览器事件循环的执行顺序是"&gt;浏览器事件循环的执行顺序是
&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;🔥 执行一个宏任务 → 执行所有微任务 → 浏览器进行一次渲染 → 再执行下一个宏任务&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;也就是说：&lt;strong&gt;微任务的优先级高于宏任务&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id="浏览器渲染时机"&gt;浏览器渲染时机
&lt;/h4&gt;&lt;p&gt;浏览器通常会在 宏任务执行完并清空微任务队列后 才进行页面渲染，所以如果微任务不断产生，可能会导致页面渲染被延迟。&lt;/p&gt;
&lt;h3 id="常见误区"&gt;常见误区
&lt;/h3&gt;&lt;h4 id="误区一同步任务是宏任务异步任务是微任务-"&gt;误区一：同步任务是宏任务，异步任务是微任务 ❌
&lt;/h4&gt;&lt;p&gt;浏览器调度任务分为宏任务和微任务，从浏览器层面而言，没有同步任务和异步任务的说法。在浏览器层面，同步和异步只是执行方式。同步代码只是 &lt;strong&gt;当前宏任务中的执行内容&lt;/strong&gt;。&lt;/p&gt;
&lt;h4 id="误区二promise-本身是微任务---promisethen是微任务-"&gt;误区二：Promise 本身是微任务 ❌ / Promise.then是微任务 ❌
&lt;/h4&gt;&lt;p&gt;Promise &lt;strong&gt;本身不是微任务&lt;/strong&gt;。Promise executor 是同步执行，Promise的then、catch、finally的&lt;strong&gt;回调&lt;/strong&gt;才是微任务。&lt;/p&gt;
&lt;h4 id="误区三async是微任务---await-是微任务-"&gt;误区三：async是微任务 ❌ / await 是微任务 ❌
&lt;/h4&gt;&lt;p&gt;这个误区其实和 “Promise 是微任务” 的误区本质类似。
async 本身并不是微任务。async 只是 JavaScript 的一个语法糖，它会让 函数返回一个 Promise。
await 也是语法糖，await = &lt;strong&gt;暂停当前 async 函数&lt;/strong&gt; + 注册一个 Promise.then。await的后面的代码才是微任务。&lt;/p&gt;
&lt;h4 id="误区四浏览器执行-js-时并不是每一行代码都是一个任务-"&gt;误区四：浏览器执行 JS 时，并不是每一行代码都是一个任务。 ❌
&lt;/h4&gt;&lt;p&gt;整个JS代码块是一个宏任务。&lt;/p&gt;
&lt;h3 id="实战"&gt;实战
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;async1&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;async2&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;async2&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; Promise.&lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;async1&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;最后输出:
1 3 5 4 2&lt;/p&gt;
&lt;p&gt;拆解:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;进入 script（一个宏任务）执行 async1()&lt;/li&gt;
&lt;li&gt;进入 async1()&lt;/li&gt;
&lt;li&gt;执行 console.log(1); 此时输出 1。&lt;/li&gt;
&lt;li&gt;执行 await async2();&lt;/li&gt;
&lt;li&gt;进入 async2()&lt;/li&gt;
&lt;li&gt;执行 console.log(3); 此时输出 3。&lt;/li&gt;
&lt;li&gt;执行 await Promise.resolve(); 由于 await = 暂停当前 async 函数 + 注册一个 Promise.then。也就是说 console.log(4); 会被注册成一个微任务。&lt;/li&gt;
&lt;li&gt;此时 async2() 被暂停，后续代码 console.log(4); 已经进入 微任务队列。&lt;/li&gt;
&lt;li&gt;async2() 返回一个 pending 的 Promise。&lt;/li&gt;
&lt;li&gt;回到 async1() 中的 await async2(); 由于 async2() 返回的 Promise 还没有 resolve，所以 async1() 也会 被暂停。&lt;/li&gt;
&lt;li&gt;async1() 后面的 console.log(2); 暂时不会执行，也不会进入微任务队列。&lt;/li&gt;
&lt;li&gt;async1() 执行结束（暂停状态），回到 script。&lt;/li&gt;
&lt;li&gt;执行 console.log(5); 此时输出 5。&lt;/li&gt;
&lt;li&gt;当前 script（宏任务）执行完毕。&lt;/li&gt;
&lt;li&gt;Event Loop 开始执行 微任务队列。&lt;/li&gt;
&lt;li&gt;执行微任务 console.log(4); 此时输出 4。&lt;/li&gt;
&lt;li&gt;async2() 执行完成，其返回的 Promise resolve。&lt;/li&gt;
&lt;li&gt;async1() 中的 await async2(); 得到结果，于是继续执行 async1() 剩余代码。&lt;/li&gt;
&lt;li&gt;console.log(2); 被加入 微任务队列。&lt;/li&gt;
&lt;li&gt;执行该微任务 console.log(2); 此时输出 2。&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>JS模块化</title><link>https://jjjjjjy.github.io/posts/javascript/javascriptmodulesystems/jsmodule/</link><pubDate>Sat, 28 Feb 2026 14:26:13 +0800</pubDate><guid>https://jjjjjjy.github.io/posts/javascript/javascriptmodulesystems/jsmodule/</guid><description>&lt;h1 id="模块化"&gt;模块化
&lt;/h1&gt;&lt;p&gt;模块化（Module）指把代码拆分成独立、可复用、可维护的单元。每个模块能够：1️⃣ 独立作用域 2️⃣ 明确依赖 3️⃣ 对外暴露接口；&lt;strong&gt;目的&lt;/strong&gt;：防止变量污染、提高代码复用、提升可维护性、支持大型工程&lt;/p&gt;
&lt;h1 id="javascript-模块化发展历史"&gt;JavaScript 模块化发展历史
&lt;/h1&gt;&lt;h2 id="阶段1iife"&gt;阶段1：IIFE
&lt;/h2&gt;&lt;p&gt;IIFE = 立即执行函数&lt;/p&gt;
&lt;h2 id="阶段2commonjs"&gt;阶段2：CommonJS
&lt;/h2&gt;&lt;p&gt;CommonJS 是 Node.js 的模块规范。&lt;/p&gt;
&lt;p&gt;特点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;同步加载（Node 可以同步读文件。浏览器不行。）&lt;/li&gt;
&lt;li&gt;运行时加载&lt;/li&gt;
&lt;li&gt;适合服务器环境&lt;/li&gt;
&lt;li&gt;值拷贝（CommonJS 导出是 值拷贝）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;CommonJS 加载过程&lt;/strong&gt;： const module = require(&amp;rsquo;./math&amp;rsquo;);&lt;/p&gt;
&lt;h2 id="阶段3amd--cmd"&gt;阶段3：AMD / CMD
&lt;/h2&gt;&lt;h3 id="amd异步模块定义asynchronous-module-definition"&gt;AMD&lt;code&gt;异步模块定义&lt;/code&gt;(Asynchronous Module Definition)
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;使用 define 定义模块&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;使用 require 加载模块&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="cmd-公共模块定义common-module-definition"&gt;CMD &lt;code&gt;公共模块定义&lt;/code&gt;(Common Module Definition)
&lt;/h3&gt;&lt;p&gt;在 CMD 规范中，一个模块就是一个文件。
&lt;strong&gt;使用 define 定义模块&lt;/strong&gt;
&lt;strong&gt;使用 SeaJs 的 use 加载模块&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="umd通用模块定义universal-module-definition"&gt;UMD&lt;code&gt;通用模块定义&lt;/code&gt;(Universal Module Definition)
&lt;/h3&gt;&lt;p&gt;UMD 是 AMD 和 CommonJS 的一个糅合。
AMD 是浏览器优先，异步加载；
CommonJS 是服务器优先，同步加载。
既然要通用，怎么办呢？
那就先判断是否支持 node 的模块，支持就使用 node；
再判断是否支持 AMD，支持则使用 AMD 的方式加载。
这就是所谓的 UMD。&lt;/p&gt;
&lt;h3 id="amd-和-cmd-的区别"&gt;AMD 和 CMD 的区别
&lt;/h3&gt;&lt;p&gt;对于依赖的模块，AMD 是 提前执行，CMD 是 延迟就近执行。
AMD是立即执行，CMD是按需执行。&lt;/p&gt;
&lt;p&gt;AMD 推崇 依赖前置，CMD 推崇 依赖就近。&lt;/p&gt;
&lt;p&gt;AMD 的 API 默认是一个当多个用，CMD 的 API 严格区分，推崇职责单一。&lt;/p&gt;
&lt;h2 id="阶段4es-module"&gt;阶段4：ES Module
&lt;/h2&gt;&lt;p&gt;ES6 模块的设计思想是尽量的 &lt;strong&gt;静态化&lt;/strong&gt;，即在&lt;strong&gt;编译阶段&lt;/strong&gt;就确定模块的依赖关系，以及输入和输出的变量。&lt;/p&gt;
&lt;p&gt;｜ CommonJS 和 AMD 模块，都只能在运行时确定这些东西。&lt;/p&gt;
&lt;p&gt;在 ES6 中，我们使用 export 关键字来导出模块，使用 import 关键字来引入模块。&lt;/p&gt;
&lt;p&gt;🔥 优点：Tree Shaking 和 编译优化&lt;/p&gt;
&lt;p&gt;ESM导出的是值引用。&lt;/p&gt;
&lt;h2 id="es6-模块与-commonjs-模块的差异"&gt;💡ES6 模块与 CommonJS 模块的差异
&lt;/h2&gt;&lt;p&gt;｜ 区别&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: center"&gt;区别&lt;/th&gt;
&lt;th style="text-align: center"&gt;CommonJS&lt;/th&gt;
&lt;th style="text-align: center"&gt;ESM&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;加载方式&lt;/td&gt;
&lt;td style="text-align: center"&gt;运行时加载&lt;/td&gt;
&lt;td style="text-align: center"&gt;编译时输出接口&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;加载方式&lt;/td&gt;
&lt;td style="text-align: center"&gt;同步加载&lt;/td&gt;
&lt;td style="text-align: center"&gt;异步加载，有一个独立的模块依赖的解析阶段。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;模块输出&lt;/td&gt;
&lt;td style="text-align: center"&gt;值的拷贝&lt;/td&gt;
&lt;td style="text-align: center"&gt;值的引用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;语法&lt;/td&gt;
&lt;td style="text-align: center"&gt;require()&lt;/td&gt;
&lt;td style="text-align: center"&gt;import&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;适用环境&lt;/td&gt;
&lt;td style="text-align: center"&gt;Node&lt;/td&gt;
&lt;td style="text-align: center"&gt;浏览器+Node&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;Tree Shaking&lt;/td&gt;
&lt;td style="text-align: center"&gt;不能，因为require是动态执行，编译期无法确定依赖&lt;/td&gt;
&lt;td style="text-align: center"&gt;可以，因为依赖关系可以静态分析&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;🌼 引申&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;require和import的区别：require()是运行时加载，import是编译时输出接口。&lt;/li&gt;
&lt;li&gt;treeShaking是在哪个阶段做的？&lt;/li&gt;
&lt;li&gt;Node 为什么最早用 CommonJS？&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;因为node读取本地文件是同步的，而CJS同步加载没有问题&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="4"&gt;
&lt;li&gt;module.exports 和 exports 的区别？&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>闭包</title><link>https://jjjjjjy.github.io/posts/javascript/closure/</link><pubDate>Sat, 28 Feb 2026 14:26:13 +0800</pubDate><guid>https://jjjjjjy.github.io/posts/javascript/closure/</guid><description>&lt;h1 id="闭包"&gt;闭包
&lt;/h1&gt;&lt;p&gt;&lt;strong&gt;闭包概念：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;闭包是函数在创建时形成的词法作用域绑定关系。&lt;/li&gt;
&lt;li&gt;本质是函数对象内部持有对其创建时作用域的引用。&lt;/li&gt;
&lt;li&gt;当函数在其定义作用域之外执行时，仍然可以访问当时的变量，这种机制就是闭包。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;📍形成闭包的必要条件：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;内部函数引用了外部变量&lt;/li&gt;
&lt;li&gt;内部函数有没有活到外层函数结束之后&lt;/li&gt;
&lt;li&gt;这个内部函数在外部仍然存在&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;📍 闭包什么时候产生？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;函数创建时就产生闭包结构&lt;/strong&gt;，但只有函数逃逸时才会保留环境。&lt;/p&gt;
&lt;h3 id="词法作用域"&gt;词法作用域：
&lt;/h3&gt;&lt;p&gt;词法作用域（Lexical Scope）是指：变量的作用域在代码“&lt;strong&gt;写出来&lt;/strong&gt;”的时候就已经确定了。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;词法作用域是指变量的作用域在代码定义阶段就已经确定，由函数的书写位置决定，而不是由调用位置决定。&lt;/li&gt;
&lt;li&gt;JavaScript 是词法作用域语言，函数在创建时会记录其外部词法环境，变量查找沿着作用域链向外进行。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;经典例子：log打印的是1，而不是2。 因为foo被定义在全局作用域，而不是定义在bar的作用域。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;a&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;foo&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;a&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;bar&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;a&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;foo&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;bar&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item></channel></rss>