递归中如何跟踪计数?为何数组可累计数字变量却失效?
递归统计嵌套对象key次数时,数字计数器失效但数组可行的原因
我是JavaScript新手,想要编写一个统计嵌套对象中特定key出现次数的函数。尝试将计数器作为函数参数初始化,但递归返回后计数器无法保留计数——递归时正确给计数器加1,但返回后新增的计数丢失。最终通过空数组解决了问题:先递归收集所有嵌套对象的key再统计数量。想请教:为何数组能跟踪累计,数字变量却不行?
无法正常工作的计数代码
const countKeysInObj = function(obj, key, count = 0) { for (let prop in obj) { if (prop === key) { console.log("counting"); count += 1; } if (typeof obj[prop] === 'object') { console.log("recursing"); countKeysInObj(obj[prop], key, count); } } return count; } var testobj = { 'e': { 'x': 'y' }, 't': { 'r': { 'e': 'r' }, 'p': { 'y': 'r' } }, 'y': 'e' }; console.log(countKeysInObj(testobj, "e")) // 返回1,预期为2
原因解析
核心是JavaScript中基本类型和引用类型的参数传递规则不同:
- 数字属于基本类型,传递参数时是值拷贝——每次递归调用,都会把当前
count的数值复制一份传给子函数,子函数里修改的只是这个副本,不会影响外层函数的count变量。你原代码里递归调用后没把返回值赋值回count,所以子函数的计数完全没被外层接收。 - 数组属于引用类型,传递参数时是引用传递——传递的是数组在内存中的地址,不管多少层递归,操作的都是同一个数组对象,所以往数组里添加元素的修改会一直保留。
修复数字计数器的写法
只要在递归调用时,把返回的计数赋值回当前的count即可:
const countKeysInObj = function(obj, key, count = 0) { for (let prop in obj) { if (prop === key) { count += 1; } // 排除null,因为typeof null也会返回object if (typeof obj[prop] === 'object' && obj[prop] !== null) { // 把递归返回的计数累加回当前count count = countKeysInObj(obj[prop], key, count); } } return count; } var testobj = { 'e': { 'x': 'y' }, 't': { 'r': { 'e': 'r' }, 'p': { 'y': 'r' } }, 'y': 'e' }; console.log(countKeysInObj(testobj, "e")) // 返回2,符合预期
数组方式的逻辑
数组能工作是因为所有递归层操作的是同一个数组,收集完所有匹配的key后再取长度:
const countKeysInObj = function(obj, key) { const keys = []; const traverse = (obj) => { for (let prop in obj) { if (prop === key) keys.push(prop); if (typeof obj[prop] === 'object' && obj[prop] !== null) { traverse(obj[prop]); } } } traverse(obj); return keys.length; } var testobj = { 'e': { 'x': 'y' }, 't': { 'r': { 'e': 'r' }, 'p': { 'y': 'r' } }, 'y': 'e' }; console.log(countKeysInObj(testobj, "e")) // 返回2
内容的提问来源于stack exchange,提问作者Evan Fralin
相关产品推荐
相关产品推荐

