JavaScript中级算法脚本调试:自定义匹配函数为何失效?
whatIsInAName函数常见问题 首先,咱们先明确需求:要从collection数组里筛选出所有包含source全部键值对的对象——意思是,返回的对象不仅要有source里的每一个属性,属性值也必须完全匹配,多其他属性没关系。
我先罗列几个开发者写这个函数时最容易踩的坑,你可以对照自己的代码看看是不是中了招:
1. 只检查属性存在,忽略值的匹配
比如你可能写了这样的逻辑:
function whatIsInAName(collection, source) { return collection.filter(obj => { for (let key in source) { if (obj[key]) { // 这里只判断属性存在,没对比值! return true; } } return false; }); }
这个错误很典型:只要对象有这个属性就直接返回true,完全没管属性值和source里的是否一致。而且更糟的是,只要找到第一个存在的属性就终止了判断,哪怕source还有其他属性没检查。
2. 使用forEach导致判断失效
如果你用了Object.keys(source)获取键数组,然后用forEach遍历检查,很容易犯这个错:
function whatIsInAName(collection, source) { const sourceKeys = Object.keys(source); return collection.filter(obj => { sourceKeys.forEach(key => { if (obj[key] !== source[key]) { return false; // forEach里的return根本不会终止filter的判断! } }); return true; // 不管有没有不匹配,最后都会走到这一步 }); }
forEach里的return只能跳出当前循环迭代,没办法终止整个遍历,也不会改变filter回调的返回值。所以哪怕有属性不匹配,最后还是会返回true,导致所有对象都被保留下来。
3. 没有提前终止不匹配的判断
比如你写了嵌套循环,但没在发现不匹配时及时跳出:
function whatIsInAName(collection, source) { let result = []; for (let i = 0; i < collection.length; i++) { let match = true; for (let key in source) { if (collection[i][key] !== source[key]) { match = false; // 这里没加break,还会继续循环其他属性,浪费性能 } } if (match) result.push(collection[i]); } return result; }
虽然这个逻辑结果是对的,但一旦发现某个属性不匹配,应该立刻跳出内层循环,不用再检查其他属性——加个break能提升效率,尤其是当source属性很多的时候。
4. 忽略了对象不存在对应属性的情况
如果source里的属性在对象中根本不存在,obj[key]会返回undefined,如果source[key]不是undefined,判断obj[key] !== source[key]是对的,但如果source[key]就是undefined呢?这时候就会误判。所以最好先检查对象是否有这个属性:
// 错误的判断方式 if (obj[key] !== source[key]) { ... } // 正确的判断方式 if (!obj.hasOwnProperty(key) || obj[key] !== source[key]) { ... }
正确的参考实现
给你两个靠谱的写法,你可以对照自己的代码找差异:
方式一:用filter + every(简洁版)
function whatIsInAName(collection, source) { const sourceKeys = Object.keys(source); // every会检查所有键是否都满足条件,有一个不满足就返回false return collection.filter(obj => sourceKeys.every(key => obj.hasOwnProperty(key) && obj[key] === source[key] )); }
方式二:用嵌套循环(易理解版)
function whatIsInAName(collection, source) { const sourceKeys = Object.keys(source); const result = []; for (const obj of collection) { let isMatch = true; for (const key of sourceKeys) { if (!obj.hasOwnProperty(key) || obj[key] !== source[key]) { isMatch = false; break; // 发现不匹配立刻跳出循环 } } if (isMatch) result.push(obj); } return result; }
你可以把自己完整的代码贴出来,我能更精准地帮你定位问题,但先对照上面的坑排查一下,应该能找到问题所在~
内容的提问来源于stack exchange,提问作者Ayudh Khajne

