freeCodeCamp JavaScript 每日挑战深度解析Challenge 21 Hex Generator 随机主导色生成【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp本篇围绕 freeCodeCamp 课程仓库中的 JavaScript 每日编程挑战第 21 题「Hex Generator十六进制色值生成器」展开完整继承原题的题目描述、验收测试与参考解答并结合该挑战在课程块结构与平台测试运行时的配置讲清「如何生成一个指定颜色通道占主导的随机六位十六进制色值」的实现思路、边界条件与常见陷阱读完后你可以直接复现该题的完整解法并理解其随机范围设计背后的数学约束。题目定位它属于哪一类挑战这道挑战的源文件位于 curriculum/challenges/english/blocks/daily-coding-challenges-javascript/6821ebf3237de8297eaee797.mdfront-matter 中的关键字段为id: 6821ebf3237de8297eaee797 title: Challenge 21: Hex Generator challengeType: 28 dashedName: challenge-21其中challengeType: 28并非随意编号。在共享配置 packages/shared/src/config/challenge-types.ts 中可以确认其语义const dailyChallengeJs 28;并且该类型在配置中被归入classic编辑器布局第 134 行[dailyChallengeJs]: classic测试展示方式为tests第 175 行[dailyChallengeJs]: tests。也就是说这是 freeCodeCamp 的JavaScript 每日挑战Daily Coding Challenge类型运行在经典单文件代码编辑器中通过 Chai 风格断言assert.equal、assert.isTrue、assert.isAbove等验证你的实现。从课程块结构看该挑战的编排定义在 curriculum/structure/blocks/daily-coding-challenges-javascript.json 中块配置包含usesMultifileEditor: true块级启用多文件编辑器能力helpCategory: JavaScript求助分类归属 JavaScriptblockLayout: legacy-challenge-list以传统挑战列表方式呈现disableLoopProtectTests: true禁用循环保护测试。在challengeOrder数组中本挑战的 id6821ebf3237de8297eaee797排在 Challenge 20Array Duplicates之后、Challenge 22Tribonacci Sequence之前正是第 21 位。值得一提的是与本题主题最接近的两道题目也都在同一个块里Challenge 23「RGB to Hex」6821ebfd237de8297eaee799.md和 Challenge 62「Hex to Decimal」构成了一条「颜色与十六进制」的练习线。题目要求完整继承原始描述原题的完整题目描述如下Given a named CSS color string, generate a random hexadecimal (hex) color code that is dominant in the given color.The function should handlered,green, orblueas an input argument.If the input is not one of those, the function should returnInvalid color.The function should return a random six-character hex color code where the input color value is greater than any of the others.Example of valid outputs for a given input:InputOutputredFF0000green00FF00blue0000FF把要求拆解成可验证的约束输入域只接受三个命名色字符串red、green、blue其他任何输入包括大小写不同的Red、空串、数字都必须走兜底分支非法输入返回字符串字面量Invalid color而不是抛出异常或返回undefined输出格式六位十六进制字符表示 RGB 三个通道主导性约束输入颜色对应的通道值必须严格大于另外两个通道greater than any of the others这比「大于或等于」更苛刻——两个弱通道相等、而主导通道更大的情况才是安全的随机性约束连续两次调用应当产生不同的色值后面的测试会验证这一点。初始种子代码即你在编辑器中看到的起点是一个什么都不做的桩函数function generateHex(color) { return color; }验收测试断言如何定义「正确」原题--hints--部分给出了 5 组 Chai 断言测试它们是本题验收标准的权威定义。下面逐条解读其技术含义。1. 非法输入直接短路assert.equal(generateHex(yellow), Invalid color);yellow不在合法集合中函数必须返回Invalid color。这要求你在做任何随机数计算之前先做输入校验否则即使后续逻辑正确也会在第一条测试上失败。2. 长度与字符集校验assert.lengthOf(generateHex(red), 6);const hex generateHex(red).toUpperCase(); const isValidHex /^[0-9A-F]{6}$/.test(hex); assert.isTrue(isValidHex);两条测试合起来约束了输出形状长度恰为 6且归一化到toUpperCase()后只允许大写字母数字字符0-9A-F。这里有两个容易踩的坑测试对返回值做了toUpperCase()才进行正则匹配说明你的实现返回大写或小写都能通过正则[0-9A-F]{6}本身不区分你产出的原始大小写如果你返回了带#前缀的字符串CSS 习惯写法lengthOf会是 7直接失败。本题要的是不带#的裸六位色值这与同块 Challenge 23「RGB to Hex」要求返回#加六位小写是两种相反的约定不要混用。3. 主导通道必须严格最大以 red 为例const hex generateHex(red).toUpperCase(); const isValidHex /^[0-9A-F]{6}$/.test(hex); assert.isTrue(isValidHex); const r parseInt(hex.slice(0, 2), 16); const g parseInt(hex.slice(2, 4), 16); const b parseInt(hex.slice(4, 6), 16); assert.isAbove(r, g); assert.isAbove(r, b);这是本题的核心数学约束。测试把六位字符串按每两位切片用parseInt(..., 16)解析出三个 0–255 的十进制通道值然后断言r g且r bassert.isAbove是严格大于。green和blue两组测试结构完全相同只是把主导断言换成g r g b和b r b g。4. 两次调用必须不同随机性const hex1 generateHex(red).toUpperCase(); // ... 解析 r1/g1/b1断言 r1 g1 且 r1 b1 const hex2 generateHex(red).toUpperCase(); // ... 解析 r2/g2/b2断言 r2 g2 且 r2 b2 assert.notEqual(hex1, hex2);这组测试同时要求两次结果都满足主导性约束且两个色值不相等。这意味着硬编码返回FF0000这类固定值能通过前三条但会在notEqual上失败如果两次调用可能随机命中完全相同的(r, g, b)组合理论上也会偶发失败虽然概率极低但随机范围设计得越窄碰撞概率越高见下文解析。参考解答逐行剖析原题--solutions--提供的官方参考实现function generateHex(color) { const toHex n n.toString(16).padStart(2, 0).toUpperCase(); const dominant Math.floor(Math.random() * 86) 170; const weak1 Math.floor(Math.random() * 170); const weak2 Math.floor(Math.random() * 170); let r, g, b; switch (color) { case red: r dominant; g weak1; b weak2; break; case green: r weak1; g dominant; b weak2; break; case blue: r weak1; g weak2; b dominant; break; default: return Invalid color; } return ${toHex(r)}${toHex(g)}${toHex(b)}; }十六进制转换工具toHexconst toHex n n.toString(16).padStart(2, 0).toUpperCase();n.toString(16)把十进制数转成十六进制字符串padStart(2, 0)保证不足两位时左补零例如9 → 09、15 → 0ftoUpperCase()统一为大写0f → 0F。三者合起来把 0–255 的十进制值稳定映射为两位大写十六进制字符。注意本题的测试对大小写不敏感toUpperCase()在这里是风格选择但如果换成大小写敏感的校验题如同块的 Hex Validator 类题目大小写约定就必须与题目一致。随机范围设计为什么是 170 和 86const dominant Math.floor(Math.random() * 86) 170; // [170, 255] const weak1 Math.floor(Math.random() * 170); // [0, 169] const weak2 Math.floor(Math.random() * 170); // [0, 169]Math.random()返回[0, 1)的浮点数Math.floor(Math.random() * N)因此落在整数区间[0, N-1]表达式取值范围作用Math.floor(Math.random() * 86) 170170 … 255主导通道Math.floor(Math.random() * 170)0 … 169两个弱通道关键约束是dominant ≥ 170而weak ≤ 169所以无论随机结果如何恒有dominant weak1且dominant weak2。这就是主导性约束assert.isAbove(r, g)与assert.isAbove(r, b)永远成立的数学保证——解法没有在事后校验或重试而是在采样范围上直接构造出必然成立的偏序关系。为什么下界取 170 而不是 255如果把主导通道固定为255纯红FF输出多样性只剩两个弱通道取[170, 255]让主导通道本身也有 86 种可能而弱通道各 170 种三者独立采样后单次调用的完整组合空间为 86 × 170 × 170 2,495,800 种notEqual(hex1, hex2)的碰撞概率约为 1/2,495,800实践中可以认为两次调用必然不同。switch 分发与默认分支switch (color)把「主导值放在哪个位置」按输入分发red时r dominantgreen时g dominantblue时b dominant其余任何值走default: return Invalid color。这个顺序与题目要求一致——先校验输入再计算随机值虽然参考实现是先算随机数再 switch但default分支在拼接前就返回了效果相同。最后用模板字符串把三段两位十六进制拼成六位结果return ${toHex(r)}${toHex(g)}${toHex(b)};padStart保证了每段恒为两位所以拼接结果恒为六位字符串assert.lengthOf(..., 6)自然满足。常见错误与等价实现结合上述测试约束列出几类典型错误写法忘记处理非法输入直接假设输入合法generateHex(yellow)返回了随机色值而非Invalid color第一条测试即失败。返回带#的字符串#ff0000长度为 7assert.lengthOf失败。主导通道与弱通道范围重叠例如三个通道都取Math.floor(Math.random() * 256)只有约 1/6 的概率恰好满足严格主导测试会大概率失败。两个弱通道与主导通道可能相等如果弱通道范围取[0, 255]弱通道可能采样到与主导通道相同的值assert.isAbove严格要求大于相等即失败。参考解法把弱通道上限压到 169、主导通道下限抬到 170正是从范围上排除这种碰撞。固定返回值硬编码FF0000能通过格式与主导性测试但两次调用结果相同assert.notEqual(hex1, hex2)失败。如果你更喜欢声明式风格下面这个等价实现用「先排通道再分配」的思路完成同样的约束同样满足全部断言function generateHex(color) { const channels { red: [2, 1, 3], green: [1, 2, 3], blue: [1, 3, 2] }; const order channels[color]; if (!order) return Invalid color; const toHex n n.toString(16).padStart(2, 0).toUpperCase(); const values [ Math.floor(Math.random() * 86) 170, // 主导170–255 Math.floor(Math.random() * 170), // 弱0–169 Math.floor(Math.random() * 170) // 弱0–169 ]; return order.map(i toHex(values[i - 1])).join(); }其正确性与官方解答相同主导值域[170, 255]与弱值域[0, 169]不相交严格主导关系恒成立order数组按 RGB 顺序取出「该位置应放主导还是弱通道」。小结与延伸阅读本题的核心不是「怎么把数字转成十六进制」而是如何在随机采样阶段就构造出必然满足偏序约束的结果主导通道[170, 255]、弱通道[0, 169]两区间不重叠使assert.isAbove恒成立而约 250 万种组合空间保证两次调用不同。验收测试Chai 断言同时校验了输入域、输出形状6 位、[0-9A-F]、严格主导性和随机性四个维度逐条对照测试写代码是解这类题最可靠的路径。相关练习与实现证据原题文件 6821ebf3237de8297eaee797.md、块结构 daily-coding-challenges-javascript.json、挑战类型定义 challenge-types.ts、同主题姊妹题 Challenge 23: RGB to Hex。【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考