freeCodeCamp 每日编程挑战 79「Navigator」详解:用数组模拟浏览器前进/后退历史
发布时间:2026/9/9 15:29:46来源:尧图网络
freeCodeCamp 每日编程挑战 #79「Navigator」详解用数组模拟浏览器前进/后退历史【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp本篇指南围绕 freeCodeCamp 开源仓库中 JavaScript 每日编程挑战Daily Coding Challenges系列的第 79 道题「Navigator」展开逐一拆解题干规则、7 组官方测试断言与参考实现并结合splice()截断前向历史等数组技巧剖析其数据结构设计。读完你将能独立实现一个基于「历史栈 当前页指针」的导航模拟器掌握模拟类算法题从题意到代码的完整拆解方法。说明freeCodeCamp 仓库中该题位于curriculum/challenges/english/blocks/daily-coding-challenges-javascript/目录下文件名68d30fc57588d97fd3027b30.md在结构清单 daily-coding-challenges-javascript.json 中被登记为Challenge 79: Navigator。题目背景与历史叙事1994 年 10 月 28 日Netscape Navigator 正式发布帮助数百万用户探索早期的 Web。这道题正是以这款划时代浏览器为背景假设你在早期浏览器里执行一串页面命令访问页面、后退、前进要求返回执行完所有命令后你当前停留在的页面。题目由此把「浏览器历史记录」这一经典交互抽象成一个纯函数问题给定命令数组commands输出当前页面名。它考察的核心是你在日常浏览网页时无意识享受的机制——后退Back与前进Forward如何在同一份历史中切换以及访问新页面时前向历史如何被清空。题目输入与规则精读函数签名为navigate(commands)从题目种子代码68d30fc57588d97fd3027b30.md可见初始实现仅是一个占位function navigate(commands) { return commands; }需要被替换为真正的逻辑。题干定义了如下不变量与三条命令要素说明初始状态永远从Home页面开始且Home不会出现在命令数组中Visit Page访问新页面。例如Visit About带你到About。访问新页面时必须丢弃discard所有前向历史Back回到历史中的上一个页面如果没有上一个页面则停留在当前页Forward沿历史前进到你之前所在的页面如果没有则停留在当前页其中Visit Page是带参数的命令命令字符串以Visit 开头后接真实页面名页面名本身可以包含空格如Visit About Us页面名为About Us甚至可以包含单词Visit本身如测试中的Visit Visit Us页面名为Visit Us解析时必须格外小心。题面给出的手推示例给定[Visit About Us, Back, Forward]应返回About Us。执行过程为访问About Us→ 后退回Home→ 再前进回到About Us。官方测试断言逐条解读该题在# --hints--段源文件 L22-L64内置了 7 组断言覆盖了前进/后退的边界与访问后截断前向历史的核心规则navigate([Visit About Us, Back, Forward])返回About Us后退再前进指针回到原页navigate([Forward])返回Home没有任何前向历史时Forward原地不动navigate([Back])返回Home初始在Home没有上一页时Back原地不动navigate([Visit About Us, Visit Gallery])返回Gallery连续两次访问落在最新页navigate([Visit About Us, Visit Gallery, Back, Back])返回Home两次后退一路退到初始页navigate([Visit About, Visit Gallery, Back, Visit Contact, Forward])返回Contact后退到About后访问新页Contact会清空About→Gallery这段前向历史因此之后的Forward无处可去、停留在Contact。这是全题最核心的规则验证点navigate([Visit About Us, Visit Visit Us, Forward, Visit Contact Us, Back])返回Visit Us注意页面名Visit Us本身含有Visit前缀验证了对命令的解析不能简单地按字符串包含关系误判。断言以assert.equal(navigate([...]), ...)的形式编写由 freeCodeCamp 的测试运行器执行用于自动判定学习者提交是否通过。解题思路为什么选「历史数组 当前页指针」浏览器历史可以建模为一维数组history其中数组中每个元素是浏览过的页面名顺序即浏览顺序用一个整型索引currentPageIndex指向当前所处位置currentPageIndex左侧是可通过Back回退的历史右侧是可通过Forward前进的历史前向历史。由此三条命令对应为Visit X先丢弃当前页之后的所有元素即前向历史再在末尾压入X指针移到新元素。若用「不可变风格」可写作history [...history.slice(0, currentPageIndex 1), X]用可变数组操作则等价于splice(currentPageIndex 1)截断 push(X)Back当currentPageIndex 0时currentPageIndex--否则不动仍在HomeForward当currentPageIndex history.length - 1时currentPageIndex否则不动。之所以不用「后退栈 前进栈」双栈是因为题目要求的语义访问新页清空前向历史、同时支持按索引直达用单数组加指针表达最直观splice一步即可完成「丢弃前向历史」。补充这道题的语法校验基于 28 号 challengeTypechallengeType: 28出现在题目 frontmatter 中整个daily-coding-challenges-javascriptblock 在 结构配置 中被标记为isUpcomingChange: true且usesMultifileEditor: true、helpCategory: JavaScript说明它属于以多功能编辑器作答、面向即将上线内容迭代的 JS 挑战块。官方参考实现逐行精解题目# --solutions--段源文件 L77-L98给出了如下参考解答function navigate(commands) { const history [Home]; let currentPageIndex 0; for (const command of commands) { if (command.startsWith(Visit)) { history.splice(currentPageIndex 1); history.push(command.slice(6)); currentPageIndex; } else if(command Back currentPageIndex 0) { currentPageIndex--; } else if (command Forward currentPageIndex history.length - 1) { currentPageIndex; } } return history[currentPageIndex]; }逐行拆解初始化const history [Home]把起始页放进数组currentPageIndex 0指向它。因此无论数组其余部分怎么变只要指针停留在 0 就必然返回Home天然满足了断言 2、3 的边界要求命令判定顺序startsWith(Visit)分支排在最前。由于Back与Forward不以Visit开头三者互斥不会误判反之页面名含Visit如Visit Visit Us仍只命中第一条分支——它仍是「访问命令」只是名字里带着Visit字样slice(6)会裁掉开头的Visit 正确得到Visit Usslice(6)的奥妙Visit 恰为 6 个字符V-i-s-i-t-空格command.slice(6)从下标 6 起截取恰好剥离命令前缀只留页面名。这也解释了为什么前缀必须是Visit 而非Visit——否则空格会残留在页面名里丢弃前向历史history.splice(currentPageIndex 1)不传第二个参数表示删除从该下标到数组末尾的全部元素一次调用同时完成清空前向历史随后history.push(...)压入新页currentPageIndex指向它带守卫的移动Back与Forward都带currentPageIndex的范围检查确保指针永不越界无法回退/前进时命令被静默忽略、停在当前页返回值循环结束后取history[currentPageIndex]即当前页。用第 6 组断言完整走查输入[Visit About, Visit Gallery, Back, Visit Contact, Forward]命令操作history指针初始—[Home]0Visit Aboutsplice(1) 无删除push[Home,About]1Visit Gallerysplice(2) 无删除push[Home,About,Gallery]2Back指针 2→1[Home,About,Gallery]1Visit Contactsplice(2) 删除Gallerypush[Home,About,Contact]2Forward2 已是末尾length-12不动[Home,About,Contact]2最终返回history[2]Contact与断言一致。可以看到正是splice(currentPageIndex 1)在Visit Contact时物理移除了已经访问过、却位于当前页之后的Gallery使Forward彻底失效——这正是题面「访问新页面时丢弃前向历史」规则的落地。第 7 组断言中的「陷阱页名」输入[Visit About Us, Visit Visit Us, Forward, Visit Contact Us, Back]Visit About Us→ history[Home,About Us]指针 1Visit Visit Us→ 先 splice(2)空再 push 由slice(6)剥出的Visit Ushistory[Home,About Us,Visit Us]指针 2Forward→ 已在末尾原地不动仍是Visit UsVisit Contact Us→ splice(3)空pushContact Ushistory[Home,About Us,Visit Us,Contact Us]指针 3Back→ 指针 3→2落回Visit Us。该断言专门验证两点页面名可以合法地包含Visit靠slice(6)而非indexOf/replace规避歧义且当没有前向历史时Forward不会导致任何跳跃。复杂度与延伸思考从实现可以看出该解法是单次遍历设命令数量为n、历史数组最大长度为n1时间复杂度O(n²)最坏情形每次Visit的splice都可能线性搬运数组平均情况下接近O(n)。对竞赛/日常训练规模题目测试中n很小完全足够空间复杂度O(n)仅需存放历史数组与指针两个状态变量。值得延伸的三点讨论模拟题的通法大量「按规则逐步推进」的题目先画状态这里是history 指针再翻译命令比直接猜公式可靠得多freeCodeCamp 该 series 中同类题目如 Challenge 78: Integer Sequence、Challenge 80 等均可参考同一目录curriculum/challenges/english/blocks/daily-coding-challenges-javascript/下的.md文件也普遍遵循「状态建模 → 逐条断言」的结构解析健壮性startsWith(Visit)slice(6)依赖严格的前缀约定若命令格式变化例如加入Visit 多空格或大小写混用则需换正则/^Visit\s(.)$/提取页面名真实浏览器实现真实浏览器的历史记录远比该模型复杂涉及重定向合并、会话恢复、标签页等但「前进列表在访问新页时清空」这一用户预期恰是本题要你在 20 行代码里重现的核心语义。如果想亲自运行这 7 组断言可将# --solutions--中的函数与各assert.equal粘贴进 Node.js 或浏览器控制台直接验证输出题目作答则需在 freeCodeCamp 的多功能编辑器中按题目页面提示完成。小结「Challenge 79: Navigator」是一道以真实浏览器历史为背景、考查数组操作与状态指针的模拟题。把握住三个要点即可秒解Home常驻历史数组首位、Back/Forward只在指针合法范围内移动、Visit必须先用splice(currentPageIndex 1)清空前向历史再入栈。本文涉及的题目源文件、结构清单与测试断言均可从 freeCodeCamp 仓库curriculum/challenges/english/blocks/daily-coding-challenges-javascript/68d30fc57588d97fd3027b30.md及其同目录文件进一步查阅。【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网