Codewars中titleCase函数报Cannot read properties of undefined错误排查
我在Codewars上完成一道题目时,本地RunJS测试所有用例都符合预期,但粘贴到Codewars编辑器运行时,出现如下错误:
TypeError: Cannot read properties of undefined (reading 'toUpperCase')
at /workspace/node/test.js:8:28
at Array.forEach (<anonymous>)
at titleCase (test.js:7:14)
at Context.<anonymous> (test.js:38:19)
at process.processImmediate (node:internal/timers:471:21)
我的实现代码:
function titleCase(title, minorWords) { if (title === "" && minorWords === "") return "" if (!minorWords) { let result1 = [] let wordArr2 = title.split(" ") wordArr2.forEach((word) => { result1.push(word[0].toUpperCase() + word.substring(1).toLowerCase()) }) return result1.join(" ") } let newTitle = title.toLowerCase() let newMinorWords = minorWords.toLowerCase() let wordArr = newTitle.split(" ") let minorWordArr = newMinorWords.split(" ") let result2 = [] wordArr.forEach((word) => { if (!minorWordArr.includes(word) || minorWordArr.includes(word[0])) { result2.push(word[0].toUpperCase() + word.substring(1).toLowerCase()) } else { result2.push(word.toLowerCase()) } }) return result2.join(" ") }
Codewars上的测试用例:
const Test = require('@codewars/test-compat'); describe("Tests", () => { it("test", () => { Test.assertEquals(titleCase(''), '') Test.assertEquals(titleCase('a clash of KINGS', 'a an the of'), 'A Clash of Kings') Test.assertEquals(titleCase('THE WIND IN THE WILLOWS', 'The In'), 'The Wind in the Willows') Test.assertEquals(titleCase('the quick brown fox'), 'The Quick Brown Fox') }); });
请问为何在Codewars中会出现该错误,而本地RunJS测试正常?
错误原因
报错的核心是空字符串的数组遍历处理:
当执行titleCase('')时,minorWords是undefined,代码会进入if (!minorWords)分支。此时title.split(" ")会返回[""](空字符串按空格分割会得到包含一个空字符串的数组)。遍历这个数组时,word是空字符串,word[0]的值为undefined,调用toUpperCase()自然会抛出Cannot read properties of undefined的错误。
你本地测试没触发这个错误,大概率是因为本地测试时,titleCase('')的执行路径被意外覆盖——比如你本地可能修改了第一个判断条件(比如直接判断title === ""就返回空),但粘贴到Codewars的代码保留了原有的title === "" && minorWords === ""的判断,导致空title且minorWords为undefined时,没有提前返回,进入了后续的错误分支。
修复方案
只需要修改第一个判断条件,让所有空title的情况直接返回空字符串,无需进入后续处理:
function titleCase(title, minorWords) { // 先判断title是否为空,直接返回 if (title === "") return "" if (!minorWords) { let result1 = [] let wordArr2 = title.split(" ") wordArr2.forEach((word) => { result1.push(word[0].toUpperCase() + word.substring(1).toLowerCase()) }) return result1.join(" ") } let newTitle = title.toLowerCase() let newMinorWords = minorWords.toLowerCase() let wordArr = newTitle.split(" ") let minorWordArr = newMinorWords.split(" ") let result2 = [] wordArr.forEach((word) => { // 额外处理空单词的情况(避免title中有连续空格的情况) if (!word) { result2.push(word) return } if (!minorWordArr.includes(word) || minorWordArr.includes(word[0])) { result2.push(word[0].toUpperCase() + word.substring(1).toLowerCase()) } else { result2.push(word.toLowerCase()) } }) return result2.join(" ") }
另外还可以在遍历数组时加入空单词的判断,避免title中存在连续空格(比如"hello world")时再次触发类似错误。
内容的提问来源于stack exchange,提问作者Cyber-SW

