ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

现代 JavaScript 教程实战:用闭包与函数对象属性为 counter 扩展 set 和 decrease 方法

2026/10/7 16:09:46 拓冰建站 浏览量
现代 JavaScript 教程实战:用闭包与函数对象属性为 counter 扩展 set 和 decrease 方法 文档教程前端【免费下载链接】zh.javascript.info现代 JavaScript 教程The Modern JavaScript Tutorial以最新的 ECMAScript 规范为基准通过简单但足够详细的内容为你讲解从基础到高阶的 JavaScript 相关知识。项目地址https://gitcode.com/gh_mirrors/zh/zh.javascript.info点击查看免费下载本篇实战文章基于《现代 JavaScript 教程》zh.javascript.info中「函数对象NFE」章节的进阶练习题「为 counter 添加 set 和 decrease 方法」围绕官方解答源码深入拆解如何在同一份计数状态上同时挂载调用即自增的计数函数与set/decrease两个操作方法。读完本文你将掌握闭包与函数对象属性两种状态保持方案的取舍理解嵌套函数与箭头函数如何共享同一个外部词法环境并学会用 Mocha 风格的单元测试验证计数器的完整行为。任务回顾让计数器能加能减能设置本练习位于仓库 1-js/06-advanced-functions/06-function-object/2-counter-inc-dec/task.md重要程度标记为importance: 5最高级。题目要求修改经典的makeCounter()工厂函数使返回的计数器在原有每次调用返回下一个数字的基础上额外支持两种操作counter()—— 返回下一个数字与原逻辑一致counter.set(value)—— 将当前计数count设置为valuecounter.decrease()—— 将count减 1。题目还特别给出提示P.S.可以使用闭包或函数属性来保持当前计数也可以两种方式混用。这实际上暗示了本练习有两条不同的实现路线正是本篇文章要展开对比的核心。练习附带的可运行沙箱初始代码见 _js.view/source.js它展示了期望的完整调用序列function makeCounter() { let count 0; // ... your code ... } let counter makeCounter(); alert( counter() ); // 0 alert( counter() ); // 1 counter.set(10); // set the new count alert( counter() ); // 10 counter.decrease(); // decrease the count by 1 alert( counter() ); // 10 (instead of 11)注意最后一步set(10)后调用一次得到10内部已自增到11随后decrease()将计数减回10因此下一次调用返回的是10而非11。官方解答闭包变量 函数属性组合方案官方解答全文见 solution.md核心源码见 _js.view/solution.jsfunction makeCounter() { let count 0; function counter() { return count; } counter.set value count value; counter.decrease () count--; return counter; }解答原文这样解释其原理该解决方案在局部变量中使用count而进行加法操作的方法是直接写在counter中的。它们共享同一个外部词法环境并且可以访问当前的count。也就是说这套方案把状态存储与行为挂载分开了状态存储count是makeCounter()调用内部的局部变量靠闭包保存在词法环境中外部代码无法直接触碰行为挂载set与decrease以箭头函数的形式作为属性直接写在函数对象counter上状态共享无论是普通函数counter还是两个箭头函数属性它们都在makeCounter()内部创建因此共享同一个外部词法环境读写的是同一个count。原理深挖为什么三个函数能共享同一个 count要真正理解这套解法的妙处需要回到闭包章节的底层机制。仓库 1-js/06-advanced-functions/03-closure/article.md 详细阐述了 JavaScript 的词法环境Lexical Environment模型每个运行的函数、代码块以及整个脚本都有一个隐藏的词法环境对象它由环境记录Environment Record存储局部变量和对外部词法环境的引用两部分组成所有函数在诞生时都会通过隐藏属性[[Environment]]记住创建它们的词法环境这个引用在函数创建时被设置并永久保存当代码访问一个变量时会先搜索内部词法环境再逐级向外搜索直到全局词法环境变量在它所在的词法环境中被更新。对照本练习的makeCounter()调用过程每次执行makeCounter()都会创建一个新的词法环境其中存放局部变量count 0函数counter、箭头函数counter.set、箭头函数counter.decrease都在这个环境中被创建三个函数的[[Environment]]全部指向同一个词法环境之后无论counter被赋值给哪个变量、在哪里被调用count的查找路径都是自身词法环境无局部变量→ 外部makeCounter()的词法环境找到countcounter()中的count、set中的count value、decrease中的count--操作的都是同一份count。这里还有一个值得注意的细节set和decrease使用的是箭头函数。箭头函数没有自己的this、arguments和super它们完全从定义处的外层作用域捕获变量。在本例中这意味着箭头函数体内的count直接绑定到makeCounter的词法环境中的那个countcount value这条赋值语句会直接修改外层词法环境中count属性的值——这正是set(10)之后counter()返回10的根本原因。同时题目解答中用到的方法直接写在counter上这一写法正是 函数对象NFE 章节 中自定义属性一节的实战延伸在 JavaScript 中函数也是对象我们可以像给普通对象增删属性一样给函数挂载属性而这些属性对函数的执行本身没有任何影响只是作为额外携带的数据或方法存在。对比方案纯函数属性保持计数题目 P.S. 中提到也可以使用函数属性来保持当前的计数。基于 函数对象NFE 章节 中函数属性有时会用来替代闭包的思路可以写出完全不使用局部变量的等价实现function makeCounter() { function counter() { return counter.count; } counter.count 0; counter.set value counter.count value; counter.decrease () counter.count--; return counter; }这一版与官方解答的行为完全一致但状态存放位置不同两种路线的差异可以总结如下对比维度闭包变量方案官方解答函数属性方案状态存放count位于makeCounter的词法环境count直接挂在函数对象counter.count上外部可访问性外部代码无法访问count只能通过三个方法间接操作外部代码可直接读写counter.count状态封装性强状态对外完全隐藏弱状态是公开的函数属性适用场景需要严格封装内部状态、防止外部篡改需要外部能直接查看/干预状态、或利用函数属性做内省函数对象NFE 章节 中也明确指出闭包变量方案下外部的代码无法访问到它只有嵌套的那些函数可以修改它而函数属性方案下外部代码可以直接执行counter.count 10之类的赋值。因此选择哪种实现方式取决于我们的需求是什么——如果需要对外隐藏内部状态就用闭包变量如果希望状态可被外部直接读写就用函数属性。用单元测试验证计数器行为本练习在 _js.view/test.js 中附带了一组 Mocha 风格的单元测试覆盖了三种行为可以作为实现是否正确的判定标准describe(counter, function() { it(increases from call to call, function() { let counter makeCounter(); assert.equal( counter(), 0 ); assert.equal( counter(), 1 ); assert.equal( counter(), 2 ); }); describe(counter.set, function() { it(sets the count, function() { let counter makeCounter(); counter.set(10); assert.equal( counter(), 10 ); assert.equal( counter(), 11 ); }); }); describe(counter.decrease, function() { it(decreases the count, function() { let counter makeCounter(); counter.set(10); assert.equal( counter(), 10 ); counter.decrease(); assert.equal( counter(), 10 ); }); }); });测试要点解读递增行为counter()连续调用依次返回0、1、2验证count返回旧值并自增的后置递增语义set 行为counter.set(10)后第一次调用返回10同时内部自增到11第二次返回11验证set确实把count改成了目标值且没有破坏自增逻辑decrease 行为set(10)后调用一次内部变为11decrease()减回10下一次调用返回10验证decrease与自增逻辑共享同一份状态。注意每个测试用例都调用一次makeCounter()创建独立的计数器这也印证了闭包章节的结论每次makeCounter()调用都会创建独立的词法环境多个计数器实例互不影响。小结本练习看似简单实则同时覆盖了本教程两个重要章节的核心知识点闭包与词法环境见 闭包章节count被保存在makeCounter的词法环境中三个函数通过[[Environment]]共享同一份状态函数对象与自定义属性见 函数对象章节set和decrease作为属性直接挂在函数对象counter上函数既是可调用的行为也是可携带方法与数据的对象。官方解答将两者结合用闭包变量保护状态用函数属性暴露操作既保持了状态封装性又让计数器拥有完整的自增 / 设置 / 减一接口。理解这一组合思路后你便掌握了在 JavaScript 中为行为对象附加状态与方法的标准范式这在函数式工具库、装饰器、计数器、缓存等场景中都非常常见。相关文件索引任务描述官方解答说明解答源码沙箱初始代码单元测试函数对象NFE 章节变量作用域闭包章节赞分享文档教程前端【免费下载链接】zh.javascript.info现代 JavaScript 教程The Modern JavaScript Tutorial以最新的 ECMAScript 规范为基准通过简单但足够详细的内容为你讲解从基础到高阶的 JavaScript 相关知识。项目地址https://gitcode.com/gh_mirrors/zh/zh.javascript.info点击查看免费下载相关推荐JavaScript 函数对象实战为闭包计数器扩展 set 与 decrease 方法en.javascript.info 示例精讲JavaScript 函数对象实战为闭包计数器扩展 set 与 decrease 方法en.javascript.info 示例精讲 闭包与函数对象属性是文档/教程前端JavaScript 函数对象实战为计数器添加 set 与 decrease 方法JavaScript 函数对象实战为计数器添加 set 与 decrease 方法 本篇文章以《现代 JavaScript 教程》中「函数对象NFE」章节的文档教程前端JavaScript 计数器进阶为 makeCounter 添加 set 与 decrease——闭包与函数属性的双方案实战JavaScript 计数器进阶为 makeCounter 添加 set 与 decrease——闭包与函数属性的双方案实战 导读 本篇文章基于 Modern文档/教程前端上一篇Rust代码保护终极指南如何安全保护Easy Rust项目知识产权下一篇Mortal麻将AI如何用Rust和强化学习打造21%胜率提升的竞技智能创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考