如何结合递归与辅助函数理解作用域/闭包?附二叉树示例解析
递归辅助函数中作用域/闭包的核心:值类型 vs 引用类型
我来帮你彻底理清这里的关键差异——你碰到的问题本质是JavaScript里值类型和引用类型的传递规则不同,这直接决定了辅助函数能不能修改父作用域的变量。
先拆解你的两个示例
示例1:为什么maxDepth始终停留在1?
先看你写的错误版本代码:
var maxDepth = function(root) { if (root === null) return 0; var maxDepth = 1; maxDepthHelper(root, 1, maxDepth); return maxDepth; }; function maxDepthHelper(tree, depth, maxDepth) { if (tree.left === null && tree.right === null) { maxDepth = depth > maxDepth ? depth : maxDepth; return; } if (tree.left) { maxDepthHelper(tree.left, depth + 1, maxDepth); } if (tree.right) { maxDepthHelper(tree.right, depth + 1, maxDepth); } }
这里的maxDepth是一个值类型(number),当你把它传给maxDepthHelper时,JS传递的是它的副本。也就是说,辅助函数里的maxDepth和父函数里的maxDepth是完全独立的两个变量——你在辅助函数里修改这个副本,根本碰不到父作用域里的那个变量。再加上错误版辅助函数没有返回修改后的副本,父函数里的maxDepth自然一直是初始的1。
而正确版本之所以能生效,是因为每次调用辅助函数时,都把返回的新值重新赋值给了父作用域的maxDepth,相当于把副本的结果“带”回了父作用域:
// 正确版里的关键步骤 maxDepth = maxDepthHelper(tree.left, depth + 1, maxDepth);
示例2:为什么数组能被辅助函数修改?
再看这个二叉搜索树的示例代码:
var kthSmallest = function(root, k) { let smallestArr = []; kthSmallestHelper(root, k, smallestArr); return smallestArr.pop() }; function kthSmallestHelper(bst, k, array) { if (bst === null) return; kthSmallestHelper(bst.left, k, array); if (array.length === k) return; array.push(bst.val); kthSmallestHelper(bst.right, k, array); }
这里的smallestArr是引用类型(Array),当你把它传给辅助函数时,JS传递的是这个数组在内存中的引用地址。也就是说,辅助函数里的array和父函数里的smallestArr指向的是同一个内存对象。所以你在辅助函数里做array.push(),本质是修改了这个共享的对象,父作用域里的数组自然也会跟着变化。
你忽略的核心规则
- 值类型(number、string、boolean、null、undefined、symbol):函数参数传递的是值的副本,函数内部修改参数不会影响外部变量,必须通过返回值把修改后的结果传递回去。
- 引用类型(object、array、function等):函数参数传递的是引用地址,函数内部对对象的修改会直接反映到外部,因为它们指向同一个内存对象。
- 闭包在这里的作用是让辅助函数能访问父作用域的变量,但能不能修改还要看变量的类型和传递规则——闭包给了访问权,但修改权由值/引用传递的规则决定。
内容的提问来源于stack exchange,提问作者Kushh
相关产品推荐
相关产品推荐

