Profile Lookup:6 条断言到稳健实现
【免费下载链接】TVBoxOSCTVBoxOSC - 一个基于第三方项目的代码库,用于电视盒子的控制和管理。项目地址: https://gitcode.com/GitHub_Trending/tv/TVBoxOSC
Profile Lookup 这道题看着只是"查通讯录",真正在考的是for循环遍历对象数组、括号动态取键、多分支return三条语法线的组合。freeCodeCamp 把它放在循环与递归章节的收尾位置,就是逼着你在一遍扫描里同时完成主键匹配和属性校验。看完下面的拆解,你能独立写出通过全部 6 条断言的函数,还知道哪一行是新手最爱写错的那行。
这道题在仓库里长什么样
id为5688e62ea601b2482ff8422b,题目主体写在 挑战文件,title就是 Profile Lookup。challengeType: 1,在 challenge-types.ts 里对应js = 1,意思是在浏览器控制台里补全一段 JavaScript 函数。dashedName是profile-lookup,forumTopicId为18259,两者都由 front-matter 记录在同一挑战文件里。- 它属于 basic-javascript 这个 block,block 配置 里标着
helpCategory: "JavaScript"。 - 在
challengeOrder中紧跟 Replace Loops using Recursion 之后,正好卡在循环与递归章节的末尾——这暗示解题默认要走一遍循环。
所以题目来自 freeCodeCamp 的 basic-javascript 块,位置靠后,是在检验你会不会"循环 + 取键 + 分支返回"。仓库里同id还有个 Lab 版(challengeType: 26,即jsLab),那是要从零搭应用的进阶形态,这里只讲经典版。
把题面拆成三张卡片(输入 / 输出 / 硬约束)
输入:函数骨架lookUpProfile(name, prop),name是要匹配的姓名,prop是运行时传入的属性名字符串。种子数据是一个对象数组,字段并不完全同构——number是字符串、likes是数组,likes里还装着不同数量的元素。
输出:命中且属性有效时,返回该属性的原值(可能是字符串,也可能是数组);name查不到时返回No such contact;name查到但属性无效时返回No such property。
硬约束:先判name再判prop,顺序不能反;取值必须用括号语法;数组要原样返回,不能join成字符串。种子代码如下,后文不再重复:
// ← 种子数据:一个"通讯录"对象数组 const contacts = [ { firstName: "Akira", lastName: "Laine", number: "0543236543", likes: ["Pizza", "Coding", "Brownie Points"], }, { firstName: "Harry", lastName: "Potter", number: "0994372684", likes: ["Hogwarts", "Magic", "Hagrid"], }, { firstName: "Sherlock", lastName: "Holmes", number: "0487345643", likes: ["Intriguing Cases", "Violin"], }, { firstName: "Kristian", lastName: "Vos", number: "unknown", likes: ["JavaScript", "Gaming", "Foxes"], }, ]; function lookUpProfile(name, prop) { // Only change code below this line // Only change code above this line } lookUpProfile("Akira", "likes");官方断言在验证什么
| 调用 | 期望 | 这条断言真正在卡什么 |
|---|---|---|
lookUpProfile("Kristian", "lastName") | "Vos" | 命中联系人后返回其字符串属性值 |
lookUpProfile("Sherlock", "likes") | ["Intriguing Cases", "Violin"] | 数组型属性值要原样返回 |
lookUpProfile("Harry", "likes") | 数组(typeof为object) | 再次确认返回的是数组而非字符串 |
lookUpProfile("Bob", "number") | "No such contact" | name找不到,即使prop合法也报联系人缺失 |
lookUpProfile("Bob", "potato") | "No such contact" | name、prop都无效时,优先报联系人缺失 |
lookUpProfile("Akira", "address") | "No such property" | 联系人存在但属性不存在时报属性缺失 |
这六条里藏着一条被强制的优先级:name一失败就立刻短路返回No such contact,prop根本不参与判断。只有name命中之后,才检查prop是不是这个联系人的有效属性,无效才轮到No such property。所以顺序是"先人后属性",写反了第 5 条必然翻车。
最小改动方案(能过,但有暗坑)
function lookUpProfile(name, prop) { for (let i in contacts) { // ← 取下标 0~3 逐个扫 if (contacts[i].firstName === name) { return contacts[i][prop] || "No such property"; // ← 命中后取值,假值兜底 } } return "No such contact"; // ← 扫完没命中,人不在通讯录 }for (let i in contacts):i取下标"0"~"3"逐个扫,扫完没命中就落到末尾。contacts[i].firstName === name:主键匹配,命中才进内层分支。contacts[i][prop]:括号语法用运行时变量prop取键,点语法做不到这一点。|| "No such property":取到undefined等假值时,兜底成"属性缺失"提示。
这个||有个暗坑:它只认"真值/假值",不认"属性到底存不存在"。假设给 Akira 加一个age: 0,再调lookUpProfile("Akira", "age")——0是假值,0 || "No such property"会返回"No such property",把合法值0误报成属性缺失。断言里恰好没覆盖假值场景,所以它能过全部测试,可数据一变就翻车。如果你在意这种边界,往下看。
工程级稳健写法
function lookUpProfile(name, prop) { for (const contact of contacts) { // ← 直接拿对象,省掉下标 if (contact.firstName === name) { if (Object.prototype.hasOwnProperty.call(contact, prop)) { return contact[prop]; // ← 只问"有没有",不问"值真假" } return "No such property"; } } return "No such contact"; }| 维度 | 最小改动方案 | 稳健方案 |
|---|---|---|
| 遍历方式 | for...in走下标i再取值 | for...of直接拿对象 |
| 属性存在性判断 | 靠undefined \|\|兜底 | hasOwnProperty显式判断 |
| 假值安全性 | age: 0会被误判 | 能返回0本身 |
| 可读性 | 一行省事的写法,语义含糊 | 意图直白,只多两行 |
两种写法都能过全部 6 条断言,差别只在没被断言盖住的场景:稳健写法把"属性存不存在"和"属性值真假"彻底解耦。你可以在控制台立刻验证——把函数单独拷进去,造一个含age: 0的contacts,分别跑两种写法,就能亲眼看到||版本返回No such property、而hasOwnProperty版本返回0;再跑lookUpProfile("Kristian", "likes")(返回数组)和lookUpProfile("Kristian", "lastName")(返回字符串),确认两类返回值。
新手最常写错的三行代码
contact.prop→contact[prop]:点语法读的是字面键"prop",参数传的"likes"永远取不到,结果恒为undefined。(挂掉第 1、2 条)
先判prop再判name:lookUpProfile("Bob", "potato")会先报No such property,而断言要的是No such contact。(挂掉第 5 条)
likes.join()或likes[0]:数组被拼成字符串或只取首元素,第 2 条的deepEqual和第 3 条的typeof都会失败。(挂掉第 2、3 条)
动手清单 + 往哪走
- 把最小改动方案的
for...in改成for...of,重跑 6 条断言确认行为一致。 - 在控制台造一个含
age: 0的contacts,对比||与hasOwnProperty两种写法的返回。 - 分别打印
lookUpProfile("Kristian", "likes")(数组)和lookUpProfile("Kristian", "lastName")(字符串),观察返回值类型。 - 故意把判定顺序写反,看第 5 条断言是怎么报错的。
练完经典版,可以转向同id的 Lab 版 Build a Profile Lookup(challengeType: 26,jsLab)从零搭查找工具,见 Lab 版。想弄清这些 front-matter 字段为什么这么写,翻 challenge-schema.js 里challengeType、dashedName的 Joi 校验规则。把for循环遍历、括号动态取键、多分支return这三条语法线走通,这道"查通讯录"才算真正吃透。
【免费下载链接】TVBoxOSCTVBoxOSC - 一个基于第三方项目的代码库,用于电视盒子的控制和管理。项目地址: https://gitcode.com/GitHub_Trending/tv/TVBoxOSC
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考