首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
深入理解JavaScript执行上下文:从创建阶段到调用栈
📅 2026/10/14 18:00:04
✍️ 爱科研究院
👁 阅读 3,247
1. 先搞清楚执行上下文到底是个什么东西1.1 一个看似简单却总让人栽跟头的概念做前端这几年我面试过不少人也带过不少新人。每次聊到 JavaScript 的基础知识执行上下文Execution Context这个话题几乎必出现。很多人能背出“执行上下文是代码执行时的环境”这句话但真让他说说代码到底是怎么跑的、变量是怎么找到的、this 为什么一会儿一个值立刻就变哑巴了。我之前在给团队做分享的时候喜欢用一个类比把执行上下文理解成“剧组拍戏时的一个拍摄现场”。每一次函数被调用就等于搭了一个新的片场里面有几样固定配置——有哪些演员到场变量、导演是谁this 指向谁、这个片场能通往哪个其他片场作用域链指向哪里。代码在这个片场里一句一句地演演完了就拆台走人。这个类比虽然粗糙但方向是对的。在 JavaScript 里程序运行的时候并不是代码走到哪儿算哪儿而是遵循一套严格的“上下文机制”。简单来说执行上下文就是代码在执行过程中所处的环境抽象它决定了当前这段代码能访问哪些变量、this 到底指向谁、以及函数之间的依赖关系怎么维护。每当你打开一个页面浏览器最先创建一个全局执行上下文每当你调用一个函数哪怕是在回调函数里、在事件处理函数里、在 Promise 的 then 里面JavaScript 引擎都会为这次调用单独创建一个新的执行上下文。这就是整个机制的起点也是理解后面所有内容的地基。1.2 执行上下文的三种类型JavaScript 中的执行上下文一共有三种这个基础分类必须刻在脑子里。第一种是全局执行上下文。它是最底层的那个整个脚本运行期间只会存在一个。浏览器环境下全局执行上下文对应的就是 window 对象Node.js 环境下对应的是 global 对象。这个上下文里创建的变量和函数挂在了全局对象上所以你在控制台直接敲一个在全局声明的变量名能直接访问到。第二种是函数执行上下文。每一次函数调用都会创建一个函数执行结束之后这个上下文就会被销毁除非有闭包把它“留住”了这点后面单独讲。函数上下文的数量不固定和调用次数直接挂钩。第三种是 eval 执行上下文。eval 函数执行时会产生自己的上下文。这个在日常开发中基本碰不到因为 eval 本身是危险且不推荐使用的。你可以了解它的存在但不用花太多精力。关键的一点是JavaScript 是单线程的同一时间只能执行一段代码所以这些执行上下文不可能同时处于“运行”状态。它们会被压进一个叫“调用栈”Call Stack的结构里谁在栈顶谁就正在执行执行完弹出下一个继续。1.3 为什么理解这个机制如此重要你可能觉得不就是一个概念嘛知道有这个东西不就行了我在实际工作和面试中得到的结论是理解不透执行上下文后面几乎所有 JavaScript 进阶知识点都会学得很虚。举个例子。你写代码时遇到一个变量它的值为什么是 undefined为什么有的变量在声明之前能用、有的却直接报错为什么箭头函数里的 this 和普通函数的 this 不一样为什么闭包能记住外层的变量这些问题答案全部藏在执行上下文的创建和执行机制里。再往大了说你写的每一个函数、每一个模块、每一个组件运行时都在被这套机制管理着。如果脑子里没有这张“运行地图”一旦遇到变量遮蔽、循环引用、意外全局变量这类问题排查起来就会像无头苍蝇一样乱撞。这个系列前面几篇已经把 JavaScript 的基础语法、原型链、事件循环这些内容讲过了这篇就专门把执行上下文这块硬骨头啃透。我会从一个一个的执行细节讲起配合能直接复制运行的代码尽量让你看完之后再遇到和上下文相关的面试题、实际 bug都能在脑子里立刻画出那张运行流程图。2. 执行上下文的生命周期创建阶段与执行阶段2.1 一个执行上下文从生到死经历什么我之前看很多人画执行上下文的图画得非常复杂各种箭头、表格铺满一屏幕。其实拆开来看一个执行上下文的一生只有两个阶段创建阶段和执行阶段。创建阶段发生在函数被调用、但函数体里的代码还没开始逐行执行的那一刻。这个阶段引擎会做三件固定的事第一确定 this 的指向。这件事在创建阶段就定下来了而且是整个执行期间不可更改的——很多人以为 this 是在调用时才临时决定的这个说法不准确准确地说是在进入这个上下文的时候就已经确定了。第二创建词法环境Lexical Environment。词法环境就是一张“变量登记表”它记录了这个上下文内部有哪些变量、哪些函数声明以及它们当前的值是什么。你可以把它理解成一个随时更新的仓库台账。第三创建变量环境Variable Environment。变量环境也是一个登记表但只存放 var 声明的变量。为什么要把 var 单独拎出来因为 ES6 引入了 let 和 const它们和 var 在作用域行为上差异巨大引擎需要在内部区分处理。创建阶段做完就进入执行阶段。执行阶段就是一行一行跑代码每执行到一条赋值语句就去更新词法环境或变量环境里对应记录的值。这张“先登记后执行”的流程就是变量提升Hoisting现象的本质来源。var 声明的变量在创建阶段被登记初始值默认置为 undefined函数声明则被整体登记引用直接指向函数对象本身。所以你在代码里写在后面的 function 声明在前面调用也不报错而 var 声明的变量在赋值之前拿到的值是 undefined而不是报错。let 和 const 就不一样了它们也在创建阶段被登记但不会像 var 那样先塞一个 undefined 进去而是进入一个叫“暂时性死区”TDZ的状态。在这个状态下访问变量引擎会直接抛 ReferenceError。这个设计堵住了很多由于“变量提升带来的隐式 undefined”导致的隐蔽 bug。2.2 用一段代码看懂创建阶段的全过程光说概念不够我拿一段实际代码来拆。var name 张三; let age 28; function sayHello() { console.log(你好 name); var hobby coding; } sayHello();这段代码运行时引擎的第一步是创建全局执行上下文。创建阶段全局的词法环境里登记了 name值初始化为 undefined、age进入 TDZ、以及函数 sayHello完整放入。变量环境里登记了 name。注意这里有个细节var name 同时出现在变量环境和词法环境的处理逻辑里看起来有点重复。实际上引擎的实现有多种方式不同浏览器也存在差异但核心行为一致var 声明会被登记初始值为 undefined且挂在全局对象上let 和 const 不会挂到全局对象上。执行阶段开始后第一行 name 张三词法环境里的 name 从 undefined 更新为 张三。第二行 age 28同样更新。第三行只是函数声明不执行函数体。然后代码执行到 sayHello()引擎为这次调用创建了一个新的函数执行上下文。创建阶段里sayHello 的上下文中登记了内部的 hobby 变量初始 undefined因为它是 var同时确定了 this 的指向——在非严格模式下this 指向全局对象严格模式下this 是 undefined。执行函数体时console.log 需要读取 name。引擎在 sayHello 自己的上下文里没找到 name 这个变量就沿着作用域链往上找找到了全局上下文里的 name输出“你好张三”。然后 hobby coding 赋值完成函数执行完毕sayHello 的执行上下文从调用栈中弹出被销毁。整个过程清晰吗如果你能跟着这段描述在脑子里画出每个阶段的状态表那理解就到位了。2.3 this 绑定的四条规则附判断顺序this 是执行上下文创建阶段最容易被搞混的部分。别慌其实规则不多核心就四条而且是有顺序的。第一条默认绑定。函数直接调用、没有任何修饰非严格模式下 this 指向全局对象严格模式下 this 是 undefined。这就是为什么 ES6 模块和严格模式下直接调用一个函数里面想通过 this 拿全局对象会拿不到的原因。第二条隐式绑定。函数作为某个对象的方法被调用比如 obj.method()那么 method 里的 this 指向 obj。这是最常用的场景也是很多人理解的“this 指向调用者”的来源。但这个规则有个坑如果把方法单独取出来再调用比如 const fn obj.method; fn()这时 fn 是一个“裸调用”走的是默认绑定this 就丢了。第三条显式绑定。用 call、apply、bind 强制指定 this 指向。这三兄弟的区别call 和 apply 都是立即执行区别只在传参方式上call 一个参数一个参数传apply 传一个数组bind 不立即执行返回一个新函数之后调用时 this 固定。第四条new 绑定。用 new 调用构造函数时this 指向新创建的那个实例对象并且如果构造函数没有显式返回对象new 表达式的结果就是 this 指向的这个对象。判断顺序也很清晰先看是不是 new 调用是就 new 绑定再看有没有 call/apply/bind有就显式绑定再看是不是对象方法调用是就隐式绑定否则默认绑定严格模式 undefined非严格模式全局对象。这个顺序值得你每次分析 this 时在脑子里过一遍比死记硬背强得多。2.4 箭头函数是规则的例外上面四条规则对于普通函数都成立但箭头函数是例外中的例外。箭头函数没有自己的 this它在定义的时候就捕获了外层作用域的 this之后任何 call、apply、bind 都无法改变它。这个特性在实际开发中是个双刃剑。优点在于在回调函数、定时器、事件监听器里头你不用再费劲 var that this 或者用箭头函数来保留外层的 this缺点在于如果你想让一个对象方法里的 this 动态指向调用者箭头函数做不到只要定义在对象字面量里它的 this 就跟着外层走。一个典型误区在对象字面量中定义箭头函数方法期望它指向这个对象结果 this 指向了对象外层。让我给你演示一下const obj { name: 测试对象, getName: () { return this.name; } }; console.log(obj.getName()); // undefinedthis 指向的是全局对象不是 obj这类代码在面试题里出现频率极高。你只要记住箭头函数里没有自己的 this它沿用的是定义位置外层的 this。就永远不会在这个问题上掉坑。3. 调用栈执行上下文如何排队协作3.1 调用栈就是一张“先进后出”的拍摄现场表单线程的 JavaScript 无法同时执行多段代码那多个执行上下文之间如何协调答案就是调用栈。调用栈实际上就是一个数组结构遵循后进先出LIFO的原则。引擎每遇到一个函数调用就创建一个新的函数执行上下文把它压入栈顶当前函数执行完毕就把栈顶的上下文弹出控制权交还给下面的那个上下文。想象一下一个俄罗斯套娃的打开过程。你打开最外层的大套娃全局上下文里面套着一个稍小的第一个被调用的函数的上下文再打开一层里面还套着更小的这个函数里又调用的另一个函数的上下文。全部打开、执行完毕之后从最小的那个开始一个一个合上直到回到最外层。我写过一个非常验证性的例子function first() { console.log(进入 first); second(); console.log(离开 first); } function second() { console.log(进入 second); third(); console.log(离开 second); } function third() { console.log(进入 third); debugger; console.log(离开 third); } first();你在浏览器里打开开发者工具跑这段代码加个 debugger 断点然后在 Call Stack 面板里就能看到从 anonymous全局到 first 到 second 到 third 的完整调用链。这个面板是理解调用栈最直观的工具比任何讲解都有效。调用栈还有一个隐含的作用它记录了代码执行的“回溯路径”。当函数里抛出异常时错误堆栈信息就是从当前的调用栈中提取的。你在控制台里看到的那个长长的报错信息就是这段调用路径的完整留痕。3.2 栈溢出的根源与防范调用栈的大小不是无限的。每个浏览器对栈深度的限制不同但大概在万级到十万级的数量区间。如果你写的代码无限递归下去最终会触发 RangeError: Maximum call stack size exceeded这个就是大家常说的“栈溢出”。栈溢出的本质很简单每一层函数调用都占一份栈空间但栈空间是有限的嵌套无限深栈迟早被填满。最常见的触发场景是递归没有写终止条件或者条件写得不对。比如function loop() { return loop(); } loop();这段代码在几毫秒之内就会让页面直接卡住然后控制台报栈溢出错误。还有一个容易忽略的场景在事件循环里同步递归制造无限循环。比如一个递归函数里每层都触发一个新的同步调用也可能爆栈。不过现在大家写递归时如果担心栈深可以用循环改写或者自己模拟一个手动栈来承接任务把递归改成迭代。这也算是面试官爱抛的一个优化点。破解栈溢出问题的排查思路通常是三步第一步打开控制台看调用栈找到最深的那个函数第二步检查递归出口条件看看是不是某个边界情况漏了 return第三步考虑是否可以用循环替代或者用大数组结合 while 循环来模拟递归。3.3 闭包为什么销毁了还能继续访问不少人学到闭包的时候开始犯迷糊因为从直觉上看函数都执行完了它的执行上下文不是弹出栈、销毁了吗为什么闭包还能访问到它内部的变量这里要澄清一个关键点执行上下文被弹出调用栈不等于它内部的所有数据都被垃圾回收了。JavaScript 引擎在做垃圾回收时会检查这个上下文创建的词法环境是否还被外部引用。如果某个内部函数被返回出去并且这个函数引用了外层函数的变量那么引擎就会把外层函数上下文中的词法环境保留下来形成一个“闭包”。用代码来演示function createCounter() { let count 0; return function() { count; return count; }; } const counter createCounter(); counter(); // 1 counter(); // 2 counter(); // 3createCounter 的上下文在第一次调用时创建运行完毕后从调用栈弹出。按理说 count 应该被回收了但是由于返回的那个匿名函数还抓着 count 不放count 就被保留在实际的闭包环境里。后续每次调用 counterJavaScript 都会创建一个新的函数执行上下文但这个新上下文的词法环境中外层引用指向的正是那份被保留下来的闭包环境所以 count 的状态得以延续。一个常见的性能教训闭包不是免费的。保留闭包意味着对应的词法环境无法被回收如果不注意大量使用闭包且长期持有函数引用会导致内存占用持续上升。比如在循环里创建闭包、把闭包挂到全局变量或者事件监听器上、组件销毁时不清理监听器这些都会埋下内存泄漏的隐患。我见过不少页面卡顿问题追根究底就是闭包把一大块数据锁住不放。4. 作用域链与变量查找机制4.1 作用域链是怎么连起来的前面提到每个执行上下文的创建阶段会生成词法环境和变量环境。但函数内部的代码不可能只在自己的环境里找变量它必然会访问外部的变量。引擎是怎么实现这个能力的靠的就是作用域链。作用域链的构建其实非常朴素在执行上下文创建的时候引擎不只是创建当前这一层的环境记录还会把“当前环境 → 外部环境 → 更外部环境 → … → 全局环境”这条链条通过引用串起来。这里特别容易混淆的一点是作用域链是定义时确定的不是调用时确定的。JavaScript 采用词法作用域Lexical Scoping意思就是函数能访问哪些外部变量取决于它在代码里被写在哪里而不是在哪儿被调用。我遇到过一个很典型的例子分享给读者时大家反应都很大const globalVar 全局; function outer() { const message outer 的 message; function inner() { console.log(message); console.log(globalVar); } return inner; } const myFn outer(); myFn();不管 myFn 在哪儿被调用它内部访问的 message 永远是 outer 里的那个 message因为 inner 定义在 outer 内部词法作用域就这么锁定了。这正是闭包能可靠工作的基石。4.2 变量查找的完整路径现在把整条查找链路串一遍。当执行到某个表达式需要读取一个变量时引擎会做这样一件事先看当前执行上下文的词法环境里有没有这个变量有就用没有就沿着 outer 引用去上一级环境的词法环境里找再没有继续往上直到全局环境如果全局都没有就报 ReferenceError严格模式或者挂一个全局变量并返回 undefined非严格模式。这个查找过程有个特点一旦找到了就立刻停止不会继续往上找。这个特点带来了一个头疼的问题——变量遮蔽Variable Shadowing。如果在当前作用域里声明了一个和外层同名的变量内层的这个就会遮蔽掉外层的外层那个在这片区域里“不可见”了。举个例子let name 外层; function show() { let name 内层; console.log(name); // 内层 } show();看起来平平无奇但很多 bug 就出在这里。你本意是想读外层的 name却在内层不小心用 let 或 const 重新声明了一个同名的结果引擎优先找到了内层的逻辑就跑偏了。还有一个更隐蔽的情况函数参数和内部声明的变量同名。如果函数里写了 var name xxx而参数也叫 name这不会报错参数会被内部声明的变量覆盖。如果你在函数里给参数重新赋值这个赋值操作生效的是参数本身不是外部同名变量。必须分清“赋值”和“声明”两个操作对作用域的不同影响。4.3 块级作用域对上下文机制的影响ES6 之前JavaScript 只有函数作用域和全局作用域var 声明的变量甚至没有“块”的概念for 循环外面还能用循环变量。ES6 引入了 let 和 const补上了块级作用域这块缺口也给执行上下文的词法环境添加了块级环境记录。块级作用域的意思是一对大括号 { } 内部用 let 或 const 声明的变量只在那个块内可见。for 循环的循环变量、if 块内部声明的临时变量都在各自的花括号范围内生效。这个机制对执行上下文的直接影响是在执行阶段引擎遇到一个块级语句时会创建一个临时的块级词法环境并把它作为当前词法环境的一部分串联进去。执行离开这个块块级环境被丢弃如果有闭包引用除外。对于执行上下文来说还有个常见考点for 循环里的 var 和 let 表现完全不同。for (var i 0; i 3; i) { setTimeout(() console.log(i)); // 3, 3, 3 } for (let j 0; j 3; j) { setTimeout(() console.log(j)); // 0, 1, 2 }第一个循环里var 声明的 i 属于函数/全局作用域只存在一个 i。循环结束后 i 3三个定时器读到的都是同一个 i输出 3 3 3。第二个循环里let 声明的 j 在每一次迭代都会创建一个新的块级词法环境每个定时器都捕获了自己那一轮的 j所以输出 0 1 2。这个例子同时考察了块级作用域、闭包和执行上下文三者面试出现率非常高。理解到这一层你会觉得这些机制其实是环环相扣的不是一堆零散的概念。5. 常见问题与调试技巧实录5.1 五道高频面试题先自己算再对答案我在面试别人时喜欢用下面这几道题来考察候选人对执行上下文的真实掌握程度。建议你先自己心算一遍再到浏览器里验证。第一题变量提升的经典陷阱console.log(a); var a 10;输出 undefined。因为创建阶段 var a 被登记且初始值为 undefined执行阶段 console.log 先执行了赋值还没跑。第二题函数声明优先于变量声明console.log(typeof foo); function foo() {} var foo bar;输出 function。创建阶段函数声明整体放入环境变量声明 var foo 被标记为已存在不再重复初始化。所以 typeof 拿到的是 function。第三题let 的暂时性死区console.log(b); let b 5;直接报 ReferenceError而不是 undefined。这就是 TDZ 的作用。执行栈在创建阶段记录 b 处于“不可访问”状态任何读取尝试都会报错。第四题this 绑定综合题const obj { name: obj, getName: function() { return this.name; } }; const fn obj.getName; console.log(fn()); // ? console.log(obj.getName()); // ?第一个输出取决于环境非严格模式输出 window 相关的值大概率 undefined 或空字符串。第二个输出 obj。fn 是裸调用this 走默认绑定obj.getName() 是方法调用this 隐式绑定到 obj。第五题闭包与循环变量const arr []; for (var i 0; i 3; i) { arr.push(function() { return i; }); } console.log(arr[1]());输出 3。因为这三个匿名函数共享了同一个全局/函数作用域里的 i创建上下文时的作用域链都是同一个循环结束 i 3所以无论取哪个都是 3。这五道题如果你能全部答对并把理由说清楚执行上下文这块的知识基本就算过关了。答错了也没关系对照着上文讲的创建阶段、作用域链、this 绑定规则回去重新推演一遍这个推演过程比答案本身更有价值。5.2 用浏览器调试工具把执行上下文“看”出来光在脑子里推演还不够我强烈建议你在浏览器里亲自“看”一遍执行上下文的生命周期。第一步打开开发者工具切到 Sources 面板找到你的 JavaScript 文件在你想观察的位置点一下行号加上断点。建议加在函数内部的第一行比如function test(a) { debugger; // 打断点 let b 2; return a b; } test(1);第二步刷新页面代码会停在 debugger 那一行。此时右侧面板会展示三块关键信息Call Stack 显示当前调用链Scope 面板显示当前作用域的所有变量及其值并且会分类显示 Global、Local、Closure 等不同的作用域层级Watch 面板可以手动输入表达式动态查看值。第三步点击“继续执行”或者逐行执行按钮你会看到 Scope 面板中变量的值随着语句一条条变化。比如一开始 b 还没执行赋值Scope 面板里可能根本看不到 b 的条目等你执行了 let b 2 这一行之后b 才出现值是 2。这就是创建阶段和执行阶段的区别在实际调试里的体现。还有一个非常实用的技巧在 console 里直接运行一个函数调用然后立刻展开错误堆栈。堆栈的每一行就是调用栈里那一层执行上下文的“身份信息”。看堆栈报错排查问题比我之前瞎猜变量值快得多。5.3 长期实践中攒下的避坑心得最后分享几条我踩过多次坑之后总结出的实用心得不一定在教科书里找得到但非常管用。第一条尽早开启严格模式。在文件顶部加上 use strict非严格模式下的隐式全局变量和 this 默认绑定行为会收敛很多。很多“变量名拼错导致意外创建全局变量”的坑严格模式直接给你报错省去漫长的排查时间。第二条注意回调函数里的 this。事件监听器、setTimeout、数组方法回调这些场景里的 this 指向各不相同。事件监听器里的 this 指向绑定事件的元素setTimeout 回调里的 this 在严格模式下是 undefined在非严格模式是全局对象。如果你下意识认为“回调函数里的 this 就是外面的 this”那就错了。解决方式无非三种箭头函数、bind 提前绑定、闭包缓存外层 this。第三条别把作用域链和原型链搞混。这是新手特别容易混的一对概念。作用域链解决的是“这个变量在哪个环境中定义”的问题原型链解决的是“对象的属性如何继承放”的问题。变量查找走作用域链属性查找走原型链两者是平行的机制。面试时这两个概念先后出现一定要在脑子里把它们分开。第四条善用命名约定区分变量层级。比如全局变量用全大写加下划线模块内私有变量用下划线开头函数内临时变量用短名字。虽然 JavaScript 不做强制但好的命名约定能让你在读到一段代码时立刻判断它的查找路径大致走多远对排查作用域引起的问题帮助极大。写在最后执行上下文这东西每个前端都会遇到但真正把它研究透彻的人并不多。我自己也是花了不少时间踩了不少变量提升、this 丢失的坑之后才逐渐把这条知识链串起来。建议你一定要动手去浏览器里调试一遍亲眼看看 Scope 面板的变化比看十篇文章都管用。这篇文章是这个系列的第四篇前面的内容如果还没看建议按顺序回头把原型链和事件循环两篇补一下因为执行上下文和它们的交集非常紧密。下一篇计划聊一聊模块化的发展历程和 ES Module 的底层机制到时会继续沿用这种带图、带调试、带实战的风格把每一个概念都讲到能直接用为止。
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/10/14 18:00:04
SkiaSharp 1.68.1 桌面视图包拆分解读:从 SkiaSharp.Views.Desktop 到按框架分发
2026/10/14 17:55:02
基于Kubernetes的CI/CD实践:从镜像构建到多环境发布
2026/10/14 17:55:02
网络安全能力可度量方法论:从等保合规到CMM成熟度演进
2026/10/14 20:11:32
机器人学导论前六讲:从坐标变换到动力学的模块化学习笔记
2026/10/14 20:11:32
条码秤快捷键传输实战:协议拆解、批量下发与踩坑排雷
2026/10/14 20:11:32
TensorFold 第一个请求:从 tensorfold pull 到 curl 对话,5 分钟部署本地 Chat 服务
2026/10/14 20:11:32
Ubuntu N100节点固件精简实战:省下450MB磁盘空间
2026/10/14 20:11:32
最大似然估计与最大后验估计:从贝叶斯原理到深度学习正则化
2026/10/14 20:06:29
STM32开发工具链全景拆解:IDE选型、GCC编译与OpenOCD调试实战
2026/10/14 0:02:01
Oracle 19c Windows客户端安装配置与连接排错指南
2026/10/14 0:02:01
React Native截图优化:解决Android兼容性问题
2026/10/14 0:02:01
金属锈蚀速率计算模型与防护方案优化实践
2026/10/13 12:25:09
流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南
2026/10/14 18:35:19
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别
2026/10/14 14:52:59
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容
2026/10/12 19:53:23
我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
2026/10/11 21:41:11
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026/10/11 23:43:10
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)