ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

freeCodeCamp 每日编程挑战解析:用 JavaScript 实现 Schema Validator(Challenge 295 实战指南)

freeCodeCamp 每日编程挑战解析:用 JavaScript 实现 Schema Validator(Challenge 295 实战指南) freeCodeCamp 每日编程挑战解析用 JavaScript 实现 Schema ValidatorChallenge 295 实战指南【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp导读本文围绕 freeCodeCamp 课程仓库中 Daily Coding Challenge每日编程挑战模块的Challenge 295: Schema Validator Part 1curriculum/challenges/english/blocks/daily-coding-challenges-javascript/6a0dcc730cb92a616f86f0bf.md展开完整讲解如何用typeof校验一个对象是否匹配{ username: string }这样的 JSON Schema这道经典入门的全部细节。读完本文你不仅能掌握该题的题意、测试用例、种子代码与参考实现还能顺着同系列 Part 2 ~ Part 6 的演进脉络理解枚举、可选字段、数组元素与嵌套对象等 Schema 校验的进阶写法并了解 freeCodeCamp 平台侧如何用 Joi 校验每日挑战数据。一、挑战背景Daily Coding Challenge 与 Schema Validator 系列在 freeCodeCamp 的课程结构中每日编程挑战是一个独立的 JavaScript 块block其块元数据定义在 curriculum/structure/blocks/daily-coding-challenges-javascript.json 中。该块共包含365 道挑战从Challenge 1: Vowel Balance到Challenge 365: The Last Challenge: Bucket Fill 3。Challenge 295 正位于这一长列表的中后段该 JSON 的第 1186~1188 行与 Challenge 296~300 共同组成了一个六连发的Schema ValidatorSchema 校验器专题挑战编号文件 ID主题Challenge 2956a0dcc730cb92a616f86f0bfSchema Validator Part 1单字段username: stringChallenge 2966a0dcc730cb92a616f86f0c0Schema Validator Part 2多字段posts、verifiedChallenge 2976a0dcc730cb92a616f86f0c1Schema Validator Part 3枚举类型RolesChallenge 2986a0dcc730cb92a616f86f0c2Schema Validator Part 4可选字段supporter?Challenge 2996a0dcc730cb92a616f86f0c3Schema Validator Part 5字符串数组badges: string[]Challenge 3006a0dcc730cb92a616f86f0c4Schema Validator Part 6嵌套对象数组users: UserProfile[]Challenge 295 作为该专题的第一题只要求校验一个字段是理解后续所有复杂 Schema 的基石。从块的配置isUpcomingChange: true、helpCategory: JavaScript、usesMultifileEditor: true、blockLayout: legacy-challenge-list可以看出该块属于仍在演进中的课程内容并使用多文件编辑器布局。二、任务说明题目与 Schema 定义题目的描述--description--非常简洁给定一个 JavaScript 对象Python 中为字典判断它是否匹配如下 Schema{ username: string }并附带一条关键规则Extra keys are allowed允许出现额外的键这两点共同界定了本题的判定语义Schema 校验是白名单式检查只要对象中username的值是字符串类型就视为匹配而无需关心对象里是否还藏着posts、followers等其他键题目采用 JSON Schema 的简化记法——键名后跟类型名string表示该字段的值必须是字符串。与真实 Schema 校验的对应关系本题的场景在真实工程中极为常见例如校验 API 请求体、校验用户配置对象、校验第三方回调数据等。简化后的{ username: string }等价于 JSON Schema 中的{ type: object, properties: { username: { type: string } }, required: [username], additionalProperties: true }其中required保证username必须存在additionalProperties: true对应Extra keys are allowed。理解了这层对应关系后续学习 JSON Schema 标准或 Zod、Joi 等校验库时就能快速迁移。三、测试用例Hints逐条解析题目通过--hints--提供了 5 个测试断言使用 Chai 的assert.isTrue/assert.isFalse它们精确刻画了匹配规则。逐一分析#测试输入期望结果判定依据1{ username: bob }trueusername是字符串且无其他字段匹配2{ username: jen, posts: 30 }trueusername是字符串posts属于额外键允许存在3{ username: }true空字符串依然是字符串类型匹配不要求非空4{ username: 7 }false数字7不是字符串类型不符5{ posts: 25 }false缺少必需字段usernameassert.isTrue(isValidSchema({ username: bob })); assert.isTrue(isValidSchema({ username: jen, posts: 30 })); assert.isTrue(isValidSchema({ username: })); assert.isFalse(isValidSchema({ username: 7 })); assert.isFalse(isValidSchema({ posts: 25 }));这组用例刻意覆盖了三个边界额外键不干扰判定用例 2这是Extra keys are allowed的直接体现空字符串是合法字符串用例 3Schema 只约束类型、不约束内容缺少必需字段必须判为不匹配用例 5说明username是必填字段仅类型对但键不存在也不行。四、种子代码与解题思路题目的种子代码--seed--/--seed-contents--给出了函数骨架function isValidSchema(obj) { return obj; }初始实现直接返回了入参对象obj这显然无法通过任何断言对象本身既不是true也不是false。解题的核心思路可以拆成三步访问目标字段通过obj.username读取对象的username属性进行类型判定用typeof obj.username判断其类型是否为string返回布尔值typeof的结果是字符串与string比较后得到布尔结果。关于typeof运算符有几个关键行为必须了解这也是整个 Schema Validator 系列反复使用它的原因typeof对基本类型的判定typeof bob string、typeof 7 number、typeof true boolean当属性不存在时obj.username的值为undefined而typeof undefined undefined它不等于string因此表达式会得到false——这正是用例 5缺少username能被正确判为false的底层原因无需额外判空即使对象本身为空{}typeof {}.username也是undefined比较结果依旧安全地返回false。五、参考实现官方 Solution题目的--solutions--部分给出了最精简且完全满足所有断言的参考实现function isValidSchema(obj) { return typeof obj.username string; }一行代码完成整个校验。其正确性可以对照全部 5 个断言逐一验证isValidSchema({ username: bob })→typeof bob string→trueisValidSchema({ username: jen, posts: 30 })→ 只看username→trueisValidSchema({ username: })→typeof string→trueisValidSchema({ username: 7 })→typeof 7 number→falseisValidSchema({ posts: 25 })→typeof undefined undefined→false为什么要用typeof而不是其他方式新手常见的错误是用obj.username的真值判断如if (obj.username)或直接比较obj.username string两者都会失败真值判断会把username: 判为false与用例 3 冲突直接与string比较是把值本身和类型名混淆永远为false而typeof是 JavaScript 内置的类型检测运算符能正确识别原始类型是这类 Schema 校验的标准工具。六、系列演进从单字段到嵌套对象Part 1 → Part 6Challenge 295 的难度被刻意控制在单字段类型校验后续五道题则在它之上逐层叠加规则。把整套系列连起来读就是一份循序渐进的对象 Schema 校验教程Part 2Challenge 296多字段与多类型Schema 扩展为三个字段要求所有字段同时满足{ username: string, posts: number, verified: boolean }参考实现用串联三个typeof判断function isValidSchema(obj) { return ( typeof obj.username string typeof obj.posts number typeof obj.verified boolean ); }其中posts: 21字符串化的数字会被判为false体现了类型必须精确匹配、不做隐式转换的原则。Part 3Challenge 297枚举类型Roles新增role: Roles字段Roles用管道符|表示或Roles user | creator | moderator | staff | admin { username: string, posts: number, verified: boolean, role: Roles }实现上先用数组保存合法取值再用Array.prototype.includes判断const roles [user, creator, moderator, staff, admin]; return ( typeof obj.username string typeof obj.posts number typeof obj.verified boolean roles.includes(obj.role) );role: guest不在枚举内、role: true类型错误都会返回false。Part 4Challenge 298可选字段supporter??表示字段可选但一旦存在就必须是指定类型supporter?: boolean参考实现用短路判断处理未定义或为布尔(obj.supporter undefined || typeof obj.supporter boolean)注意supporter: true字符串仍会判为false——可选不等于宽松。Part 5Challenge 299字符串数组badges: string[][]表示字段应为字符串数组可为空数组badges: string[]实现要点是Array.isArray加上every逐个元素检查类型Array.isArray(obj.badges) obj.badges.every(b typeof b string)badges: []合法空数组通过everybadges: [first-post, 18]非法混入数字。Part 6Challenge 300嵌套对象数组users: UserProfile[]这是系列的收官题将前面定义的全部规则封装成UserProfile类型再要求users是该类型的对象数组UserProfile { username: string, posts: number, verified: boolean, role: Roles, supporter?: boolean, badges: string[] } { users: UserProfile[] }参考实现把字段校验逻辑提取为内部函数isValidUser再配合Array.isArray与everyfunction isValidSchema(obj) { const roles [user, creator, moderator, staff, admin]; function isValidUser(user) { return ( typeof user.username string typeof user.posts number typeof user.verified boolean roles.includes(user.role) (user.supporter undefined || typeof user.supporter boolean) Array.isArray(user.badges) user.badges.every(b typeof b string) ); } return Array.isArray(obj.users) obj.users.every(isValidUser); }users: []合法users是单个对象而非数组、数组中混入数字徽章、缺少username、role不在枚举内等情况均判为false。这种递归复用校验函数的模式与真实项目中 Zod / Joi / TypeScript 类型体操处理嵌套结构的手法一脉相承。七、平台侧实现挑战数据如何被校验作为 freeCodeCamp 开源仓库的一部分Daily Coding Challenge 不只是静态 Markdown 文件它在运行时还有完整的平台支撑API 服务api/src/daily-coding-challenge/目录routes、schemas、utils 与 README.md提供获取每日挑战信息的端点README 明确指出获取每日挑战信息的端点每日挑战提交仍位于 API 主体部分前端数据校验客户端在 client/src/utils/daily-coding-challenge-validator.ts 中用Joi定义并校验从数据库取回的挑战数据结构——要求id、challengeNumber正整数且 ≥1、title、date、description以及javascript/python两个语言版本各自包含tests数组与challengeFiles数组都必须存在。这与本题用代码校验对象是否匹配 Schema的练习思路形成呼应平台在幕后同样在做 Schema 校验只是换成了工业级校验库挑战类型本挑战的 frontmatter 声明challengeType: 28标识其为每日编程挑战类型块配置中的disableLoopProtectTests: true表示测试不受循环保护限制方便编写涉及迭代的测试。八、动手验证本地快速测试你的实现无需启动整个平台你也可以在本地 Node.js 环境验证答案。将题目中的断言转换成一段自测脚本function isValidSchema(obj) { return typeof obj.username string; } const tests [ [{ username: bob }, true], [{ username: jen, posts: 30 }, true], [{ username: }, true], [{ username: 7 }, false], [{ posts: 25 }, false] ]; tests.forEach(([input, expected], i) { const result isValidSchema(input); console.log(Test ${i 1}: ${result expected ? PASS : FAIL} (got ${result}, expected ${expected})); });运行node test.js后应看到 5 个PASS。你可以在此基础上自行扩展边界用例例如null、undefined、字符串包装对象new String(bob)注意typeof new String(bob)的结果是object会判为false加深对typeof局限性的理解。九、小结Challenge 295 用一道极简的题目讲透了对象 Schema 校验的三大核心原则按字段检查、用typeof判类型、允许额外键。以它为起点Part 2~6 依次引入了多字段、枚举、可选字段、数组元素与嵌套对象构成了一条完整的 Schema 校验学习路径。而在 freeCodeCamp 的工程实现中同样的思路被 Joi前端挑战数据校验与 API 服务每日挑战信息分发所承接——这正是课程练习与工程实战相互印证的典型案例。后续可以继续深入阅读仓库中的相邻挑战文件如 Challenge 296、Challenge 297 至 Challenge 300或对比 client/src/utils/daily-coding-challenge-validator.ts 中 Joi 版校验写法体会手写校验与声明式校验库的异同。【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表